Olá pessoal, tudo bom?
Esse post é uma continuação do Post sobre Construtores – Parte I que pode ser acessada aqui.
Neste post iremos continuar vendo algumas implicações que podem ocorrer dentro dos construtores de uma classe durante a interpretação dos diagramas de classes UML de uma camada Model.
Associações Obrigatórias Bidirecionais
Uma associação Bidirecional, de acordo com o nosso artigo sobre associações, permite que as classes que a compõem possuírem o conhecimento entre si através de atributos internos dentro das classes que representam a outra classe. Uma associação é obrigatória quando existe a necessidade para a construção da instância de uma classe que esta instância receba instâncias de outras classes. Essas outras instâncias auxiliam na construção de regras negociais existentes para a classe que está recebendo as instâncias.
É importante frisar aqui é uma associação entre classes, unidirecional ou bidirecional, é caracterizada por duas partes. A primeira parte é a preparação das classes para estabelecer as necessidades da associação, criando atributos que representam as associações, sejam eles simples ou coleções. A segunda parte é a criação de um código que auxilie no estabelecimento da conexão entre as instâncias da classe através de seus atributos. Se esse código não for implementado não será possível estabelecer durante a execução do programa, pois pode ser que nenhum lado da associação esteja sendo conectado a outro lado.
Construindo uma associação – Parte 1: Criando os atributos e o construtor base
Essa primeira parte da construção de uma associação está relacionada com a criação dos atributos para referenciar a associação, criando os atributos que representam as associações. Observe a Figura 01 abaixo que representa uma associação bidirecional entre as classes Horario e Rota.
Essa associação bidirecional pode ser desmembrada em duas associações unidirecionais conforme mostrado pela Figura 02.
Pensando nos conceitos já ensinados na parte I deste post, como fica o construtor da classe Rota? A classe rota possui alguma associação obrigatória? Ele receberá algum parâmetro em seu construtor?
O construtor da classe Rota não deverá receber nenhum parâmetro, pois sua associação com Horário representa uma lista, cujo cardinal inicial é 0 (Zero), logo não há a necessidade de passar informações para esse construtor. Assim o código da classe Rota terá, por enquanto, o seguinte aspecto conforme Listagem 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Rota { private List<Horario> horarios; public Rota(){ //por enquanto nenhuma operacao interna } public Horario getHorarios(){ return horarios; } public void setHorarios(List<Horario> horarios){ this.horarios = horarios; } } |
Legal, classe Rota codificada. Ela possui uma ligação com a classe Horario através do atributo horarios criado. Isso teoricamente representa um lado da associação. Vamos agora criar o código da outra classe, Horário. Essa classe possui uma associação obrigatória, pois é necessário que ao criar uma nova instância de Horario informar a qual Rota ele pertence. Assim o construtor da classe Horario deverá receber uma instância de Rota para contemplar essa obrigação. Além disso deverá ser criado um atributo Rota, o qual representará a associação com a classe Rota. Vejamos o código na Listagem 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Horario { private Rota rota; public Horario(Rota rota){ //por enquanto nenhuma operacao interna } public Rota getRota(){ return rota; } public void setRota(Rota rota){ this.rota = rota; } } |
Se você for um leitor com alguma experiência estará estranhando que eu não escrevi a linha this.rota = rota. Calma isso será realizado na segunda parte junto com a explicação. Nesse momento nós encerramos a criação dos atributos básicos e seus acessos representando ambos os lados da associação Bidirecional.
Construindo uma associação – Parte 2: Estabelecendo a conexão entre as instâncias
Agora é hora de criar um códgio que estabeleça a conexão entre as instâncias das classes que compõem a associação. Vamos pensar novamente na Figura 02 com a associação bidirecional desmembrada em duas associações unidirecionais.
Conforme foi descrito na parte 1, o construtor da classe Horario deverá receber uma instância da classe Rota. Para estabelecer a conexão entre as instâncias de Horario e Rota, basta atribuir ao atributo interno da classe Horario que representa a classe Rota, conforme mostra o código destacado abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Horario { private Rota rota; public Horario(Rota rota){ this.rota = rota; } public Rota getRota(){ return rota; } public void setRota(Rota rota){ this.rota = rota; } } |
A codificação da linha 5, que você já deve estar acostumado a fazer ao criar um novo construtor Java, realiza a associação da linha vermelha da Figura 3.
Mas por que só realiza essa linha? Vamos lembrar que estamos em uma associação bidirecional. Pense mais profundamente, o que uma associação bidirecional realmente significa? Significa que se você quer saber quais são os horários existentes dentro de uma Rota, basta codificar um getHorarios(), que você obterá. Além disso se você estiver em um horário e quiser saber qual é a Rota dele, basta codificar um getRota().
Assim se você codificou a linha do this.rota = rota; apenas a instância de Horario conhece a Rota, visto que a instância de Rota não possui o conhecimento da instância de Horário que acabou de ser criada, tornando assim uma associação que deveria ser bidirecional em unidirecional.
A resolução disso pode ser feita dentro do próprio construtor de Horario, bastando acrescentar a instância do Horario à lista de Horarios existente dentro da classe Rota. Isso está representado na linha em destaque do código abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Horario { private Rota rota; public Horario(Rota rota){ this.rota = rota; this.rota.getHorarios().add(this); } public Rota getRota(){ return rota; } public void setRota(Rota rota){ this.rota = rota; } } |
Dessa forma a linha destacada está realizando o outro lado da associação completando assim ambos os lados da associação.
Pergunta:
Baseado na representação em código das classes Horario e Aviao realizada acima, o código abaixo irá gerar erro? Resposta no fim do artigo 🙂
1 2 |
Rota rota = new Rota(); Horario horario = new Horario(rota); |
Associações Obrigatórias Bidirecionais – Ambos os sentidos
No tópico anterior foi analisada uma associação bidirecional em que apenas um dos lados era obrigatório na associação. Agora vamos pensar na seguinte associação, Figura 04, entre a classe Pessoa e a classe CPF:
Assumindo os conceitos de obrigação entre classes apontados até aqui, como ficariam os códigos da classe PEssoa e da classe CPF? Vamos chechar nas Listagens abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Pessoa { private CPF cpf; public Pessoa(CPF cpf){ this.cpf = cpf; } public CPF getCpf(){ return cpf; } public void setCpf(CPF cpf){ this.cpf = cpf; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class CPF { private Pessoa pessoa; public CPF(Pessoa pessoa){ this.pessoa = pessoa; } public getPessoa(){ return pessoa; } public setPessoa(Pessoa pessoa){ this.pessoa = pessoa; } } |
Vamos instanciar um novo objeto Pessoa? De acordo com o código acima, é necessário informar uma instância da classe CPF.Veja no código abaixo como ficaria isso:
1 |
Pessoa pessoa = new Pessoa(cpf); |
OK. Mas o objeto cpf não existe, vamos então criar uma linha para instânciar esse objeto cpf, lembrando que é necessário passar uma instância da classe Pessoa:
1 2 |
CPF cpf = new CPF(pessoa); Pessoa pessoa = new Pessoa(cpf); |
Legal, mas a pessoa que está sendo passada para a criação do CPF ainda não foi instanciada, então vamos instanciá-la:
1 2 3 |
Pessoa pessoa1 = new Pessoa(cpf1); CPF cpf = new CPF(pessoa1); Pessoa pessoa = new Pessoa(cpf); |
Ok. Mas de acordo com esse código voltamos ao problema inicial de instanciar uma pessoa por não termos o cpf. Esse problema recursivo irá continuar ocorrendo até o fim dos tempos, ou da memória Heap do Java… Outra forma de ver o problema:
1 2 |
//Isso se estendendo até o fim dos tempos, ou da memória do Heap do Java... new Pessoa(new CPF(new Pessoa(new CPF(new Pessoa(new CPF(new Pessoa(new CPF(...)))))))); |
Para resolver momentaneamente esse problema é necessário que um dos lados da associação passe a não aceitar o objeto da outra em seu construtor. Ou o problema do “Ovo e Galinha, quem nasceu primeiro?”. O lado a ser escolhido deve ser baseado no escopo do projeto, ou seja, no grau de importância que cada classe tem ou qual poderia ser criada antes sem impacto as regras de negócio.
Pensando na associação entre Pessoa e CPF. É possível que uma pessoa exista sem possuir um cpf? E o contrário, é possível que exista um cpf sem uma pessoa existir? A primeira questão é verdadeira, já que uma pessoa nasce e depois de um certo tempo é realizada a confecção do documento do CPF. A segunda questão é falsa, a não ser que seja um laranja de uma obra de corrupção, mas um documento CPF deve pertencer a uma pessoa que exista. Assim o círculo vermelho, Figura 05, indica que o lado da pessoa deve ceder, ou seja, não receber o parâmetro CPF em seu construtor:
Assim o código da classe Pessoa não conterá o parâmetro cpf e passará a ser:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Pessoa { private CPF cpf; public Pessoa(){ //por enquanto nao faz nada } public CPF getCpf(){ return cpf; } public void setCpf(CPF cpf){ this.cpf = cpf; } } |
Mas ainda é necessário manter a bidirecionalidade da associação conforme visto no tópico anterior. Assim é necessário que a classe CPF ao receber uma instância de Pessoa, realize a atribuição deste nos dois sentidos da associação, caracterizando a bidirecionalidade, conforme o código abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class CPF { private Pessoa pessoa; public CPF(Pessoa pessoa){ this.pessoa = pessoa; this.pessoa.setCpf(this); } public getPessoa(){ return pessoa; } public setPessoa(Pessoa pessoa){ this.pessoa = pessoa; } } |
Repare somente nas diferenças entre as atribuições, visto que no primeiro exemplo Horario x Rota, era uma lista de Horarios como atributo da classe Rota, sendo necessário obter a lista e adicionar a nova instância de Horario.
Já no caso da associação Pessoa x CPF, o cpf é um atributo “simples”, não uma lista, bastando chamar o método setCpf() para adicionar a nota instância de CPF.
Resposta da Pergunta:
Baseado na representação em código das classes Horario e Rota realizada acima, o código abaixo irá gerar erro?
1 2 |
Rota rota = new Rota(); Horario horario = new Horario(rota); |
Sim irá gerar um NullPointerException. Por que? Você não inicializou a lista de horários na classe Rota 😉
finally{
Neste tutorial foi explicado mais a fundo a segunda parte dos construtores e associações. Espero que as principais dúvidas tenham sido sanadas.
Duvidas ou sugestões? Deixe seu feedback! Isso ajuda a saber a sua opinião sobre os artigos e melhorá-los para o futuro! Isso é muito importante!
Até um próximo post!
Leave a Reply