JavaFX: Implementando sistemas com CDI 1.1

Veja nesse artigo como criar uma aplicação desktop utilizando o Weld integrado ao JavaFX e como implementar um sistema usando CDI 1.1.

Fique por dentro
Neste artigo será apresentada uma implementação funcional de uma aplicação em JavaFX para Desktop que utiliza o Weld como contêiner de injeção de dependências. A aplicação que será desenvolvida é um jogo de Batalha Naval.

Por meio deste exemplo, conheceremos uma solução em Java útil para a criação e atribuição de objetos que tenham interdependência entre si. Essas dependências representam o relacionamento dos objetos em um sistema, relacionamento este que pode se tornar bastante complexo para ser realizado manualmente pelo programador.

Nestes casos, a injeção de dependências com CDI pode ser empregada para automatizar esta tarefa.

A injeção de dependências é um padrão de desenvolvimento idealizado para facilitar a implementação de sistemas orientados a objetos. Assim como outros padrões de projeto, surgiu de um problema recorrente no desenvolvimento de software, mas antes de entrarmos na explicação acerca deste padrão, é importante relembrar rapidamente alguns conceitos relacionados à própria Orientação a Objetos e a alguns padrões de projeto que também são pré-requisitos para o entendimento da solução apresentada neste artigo, um projeto de um Jogo de Batalha Naval.

A orientação a objetos permite que um sistema seja modelado de acordo com entidades do mundo real por meio de classes, objetos, atributos e métodos.

Neste contexto, um objeto é composto basicamente por métodos, que representam comportamentos, e atributos, que representam propriedades.

Vale lembrar também que objetos são instâncias materializadas a partir de uma classe, as quais podem ser vistas como “modelos”. Essa característica permite modelar um sistema o mais próximo possível da realidade negocial.

Por exemplo, é possível abstrair uma classe para representar um carro. Assim, os modelos de diversas marcas podem ser vistos como instâncias de um carro, onde cada uma tem suas características como a potência do motor, quantidade de portas, bem como são capazes de desenvolver comportamentos específicos, como acelerar, ligar a ignição, parar, etc.

Os comportamentos de um objeto devem refletir funções que existem no mundo real. Seguindo esta abordagem, cada objeto deve contemplar funções relacionadas a tarefas que possam ser descritas com apenas um verbo (andar, verificar, checar e etc.) para facilitar o entendimento do código, bem como a manutenção e/ou evolução do mesmo.

Aplicando esta metodologia durante o desenvolvimento, uma classe pode ser quebrada em outras, reduzindo e dividindo a solução em pedaços mais compreensíveis. Por exemplo, suponha que o carro do exemplo anterior seja um objeto que possua o método acelerar e ligar o farol.

Neste caso, é claro que ambas as funções estão disponíveis em um carro. No entanto, estes comportamentos tornam-se mais intuitivos se for possível distinguir que acelerar é uma função relacionada com o motor do carro e ligar o farol é uma função do sistema elétrico do veículo. Esta constatação deve conduzir o implementador a refatorar a classe inicial, resultando em mais dois objetos que compõem o carro: O Sistema Elétrico e o Motor, os quais devem receber os comportamentos citados.

A reorganização do código é uma técnica chamada de refatoração (refactoring), empregada para melhorar a organização das classes de um sistema orientado a objetos. Classes, métodos e atributos tendem a se modificar constantemente, gerando novos objetos e reduzindo outros, à medida que o problema é melhor compreendido.

Esta dinâmica é importante para um projeto OO, pois ajuda a manter o relacionamento do sistema com a realidade. A modelagem deve ser atualizada constantemente mesmo após a construção do software, para que continue sempre refletindo nomes atuais, ou seja, se o negócio muda a ponto de mudar o nome de uma entidade previamente modelada e tratada pelo sistema, essa entidade deve ser ajustada e renomeada para contemplar a atualização do negócio, ainda que seja uma mera mudança de nomenclatura.

Esta recomendação é necessária para que implementadores e usuários conversem sempre utilizando o mesmo vocabulário, facilitando o entendimento de ambas as partes. Em contrapartida, a refatoração pode causar instabilidade no sistema e a melhor forma de rastrear os erros decorrentes é pela implementação de testes automatizados.

De maneira sucinta, a modelagem de classes orientada a objetos é uma tarefa realizada após a identificação dos requisitos e consiste na criação de um modelo de domínio, inclusão dos comportamentos necessários e definição das mensagens a serem trocadas entre os objetos envolvidos.

Não obstante, durante a fase de projeto e implementação existem outros desafios a serem enfrentados pelo desenvolvedor, como a decisão de como e onde colocar determinado método, ou qual objeto será o proprietário de certa informação. Estes questionamentos muitas vezes não são triviais, contudo, os padrões GRASP auxiliam o projetista de software ou o programador a atribuir corretamente as competências de cada objeto.

A aderência ao princípio da alta coesão requer que cada classe, atributo ou método contenha exclusivamente responsabilidades relacionadas, ou seja, cada elemento deve executar o mínimo de funções possíveis, evitando a criação de objetos ou métodos multifacetados (que desempenham múltiplas funções).

O excesso de responsabilidades em um método ou classe dificulta a compreensão, prejudica o reuso, além de desencorajar qualquer tipo de mudança no código.

O padrão Especialista na Informação (Information Expert) é outro princípio GRASP e possui complementariedade com o padrão de Alta Coesão, pois mesmo quando as responsabilidades são corretamente divididas em pequenos blocos, haverá também a necessidade de decidir qual é o melhor local para colocá-los.

Esse padrão orienta que um novo elemento (método ou atributo) a ser incluído deve ser colocado em um local onde seja possível preencher a maioria das informações necessárias a ele. Como exemplo, considere o diagrama de classes da Figura 1.

Figura 1. Relacionamento muitos-para-muitos entre as classes Pedido e Produto

Neste cenário é preciso implementar a contagem do total de unidades vendidas de um certo produto em cada pedido realizado.

Tendo somente a descrição apresentada é possível inferir que o novo método pode ser implementado em qualquer uma das classes citadas, contudo, ao observar as indicações de navegabilidade na "

[...] continue lendo...

Artigos relacionados