Lei de Demeter, Aggregate, Delegate e interface "inchada"

Olá amigos do fórum,

Gostaria de uma ajuda/sugestões com a modelagem de um objeto aqui. Não estou satisfeito com a forma que implementei.

Tenho um objeto Endereco, que possue os atributos cep, logradouro, cidade, etc.
Tenho um objeto Telefone, que possue os atributos ddd e numero.

Por fim, tenho um objeto Usuario que tem um Endereco e um Telefone.

Minha idéia era que o Usuario atuasse como um Aggregate, encapsulando um endereco e um telefone, e, aplicando a lei de Demeter, fizesse todos os acessos pertinentes a esses objetos. O resultado final foi algo proximo disso:

public class Usuario {
    private Endereco endereco;
    private Telefone telefone;

   public String getDDD(){
       return telefone.getDDD();
   }
   public String getNumeroTelefone(){
       return telefone.getNumero();
   }
   public String getCep(){
       return endereco.getCep();
   }
   public String getLogradouro(){
      return endereco.getLogradouro();
   }
   //outros delegate methods retornando as propriedades do endereco
}

O objetivo de encapsular o Endereco e Telefone foi atingido, mas estou achando que a classe Usuario terminou com getters DEMAIS…e preciso colocar mais objetos dentro do Usuario, se eu continuar a modelagem dessa forma a classe vai terminar com trocentos metódos getters! :frowning: …não tá muito legal né?

Podem contribuir com sugestoes nesse cenário?

Obrigado!

[quote=alias]Olá amigos do fórum,

Gostaria de uma ajuda/sugestões com a modelagem de um objeto aqui. Não estou satisfeito com a forma que implementei.

Tenho um objeto Endereco, que possue os atributos cep, logradouro, cidade, etc.
Tenho um objeto Telefone, que possue os atributos ddd e numero.

Por fim, tenho um objeto Usuario que tem um Endereco e um Telefone.

Minha idéia era que o Usuario atuasse como um Aggregate, encapsulando um endereco e um telefone, e, aplicando a lei de Demeter, fizesse todos os acessos pertinentes a esses objetos. O resultado final foi algo proximo disso:
[/quote]

Vc está confundindo o que significa “Agregate”.
Agregado é um objeto que é composto de outros e que cuja vida desses outros está relacionada à do agregado. O exemplo classico é Pedido-Itens.

Endereçoe e Telefone são propriedades do usuário. Só isso. É uma simples composição. Não ha controle de ciclo de vida.
Isso que vc tentou de colocar as cmadas no usuário é simplesmente errado.

Primeiro vc cria o objeto endeeço e telefone separadamente.Depois dá um usuário.setEndereco e usuario.setTelefone. Simples. Como se fose uma string ou uma data.

Note que a relação pessoa -> endereço não é 1 para 1. É uma para muitos. O mesmo com telefone. E vc precisa de um qualificador. Por exemplo, edereço de correspondencia, endereço de cobrança, endereço de entrega. Telefone celular, Telefone comercial, telefone de emergencia, telefone de cada , etc… Esta relação mais real, também lhe indica que realmente o usuário não é um agregado desses objetos. Ele é relacionado a esses objetos, mas não é dono deles.

[quote=sergiotaborda][quote=alias]Olá amigos do fórum,

Gostaria de uma ajuda/sugestões com a modelagem de um objeto aqui. Não estou satisfeito com a forma que implementei.

Tenho um objeto Endereco, que possue os atributos cep, logradouro, cidade, etc.
Tenho um objeto Telefone, que possue os atributos ddd e numero.

Por fim, tenho um objeto Usuario que tem um Endereco e um Telefone.

Minha idéia era que o Usuario atuasse como um Aggregate, encapsulando um endereco e um telefone, e, aplicando a lei de Demeter, fizesse todos os acessos pertinentes a esses objetos. O resultado final foi algo proximo disso:
[/quote]

Vc está confundindo o que significa “Agregate”.
Agregado é um objeto que é composto de outros e que cuja vida desses outros está relacionada à do agregado. O exemplo classico é Pedido-Itens.

Endereçoe e Telefone são propriedades do usuário. Só isso. É uma simples composição. Não ha controle de ciclo de vida.
Isso que vc tentou de colocar as cmadas no usuário é simplesmente errado.

Primeiro vc cria o objeto endeeço e telefone separadamente.Depois dá um usuário.setEndereco e usuario.setTelefone. Simples. Como se fose uma string ou uma data.

Note que a relação pessoa -> endereço não é 1 para 1. É uma para muitos. O mesmo com telefone. E vc precisa de um qualificador. Por exemplo, edereço de correspondencia, endereço de cobrança, endereço de entrega. Telefone celular, Telefone comercial, telefone de emergencia, telefone de cada , etc… Esta relação mais real, também lhe indica que realmente o usuário não é um agregado desses objetos. Ele é relacionado a esses objetos, mas não é dono deles.

[/quote]

Oi sergiotaborda, obrigado pelas sua resposta.

Talvez não tenha explicado bem o cenário, pois no meu dominio a situaçao é justamente essa que você citou do agregado. Eles pertencem ao Usuario, efetivamente. Não há sentido no meu modelo ter um Telefone que nao pertença a um usuário, e nem um Endereço que não pertença a um usuário. Crio-os em conjunto, e removo-os em conjunto também (e também o usuário de fato só tem UM telefone e um endereço, entendo o que disse mas no meu domínio não faz sentido ter qualificadores pra esses caras).

Devido a esses detalhes que quis fazer o Usuario encapsular esses objetos, e não um “setEndereco” ou “setTelefone”. Dado esse cenário você ainda acha melhor fazer os setters?

Obrigado!

[quote=alias][quote=sergiotaborda][quote=alias]Olá amigos do fórum,

Gostaria de uma ajuda/sugestões com a modelagem de um objeto aqui. Não estou satisfeito com a forma que implementei.

Tenho um objeto Endereco, que possue os atributos cep, logradouro, cidade, etc.
Tenho um objeto Telefone, que possue os atributos ddd e numero.

Por fim, tenho um objeto Usuario que tem um Endereco e um Telefone.

Minha idéia era que o Usuario atuasse como um Aggregate, encapsulando um endereco e um telefone, e, aplicando a lei de Demeter, fizesse todos os acessos pertinentes a esses objetos. O resultado final foi algo proximo disso:
[/quote]

Vc está confundindo o que significa “Agregate”.
Agregado é um objeto que é composto de outros e que cuja vida desses outros está relacionada à do agregado. O exemplo classico é Pedido-Itens.

Endereçoe e Telefone são propriedades do usuário. Só isso. É uma simples composição. Não ha controle de ciclo de vida.
Isso que vc tentou de colocar as cmadas no usuário é simplesmente errado.

Primeiro vc cria o objeto endeeço e telefone separadamente.Depois dá um usuário.setEndereco e usuario.setTelefone. Simples. Como se fose uma string ou uma data.

Note que a relação pessoa -> endereço não é 1 para 1. É uma para muitos. O mesmo com telefone. E vc precisa de um qualificador. Por exemplo, edereço de correspondencia, endereço de cobrança, endereço de entrega. Telefone celular, Telefone comercial, telefone de emergencia, telefone de cada , etc… Esta relação mais real, também lhe indica que realmente o usuário não é um agregado desses objetos. Ele é relacionado a esses objetos, mas não é dono deles.

[/quote]

Oi sergiotaborda, obrigado pelas sua resposta.

Talvez não tenha explicado bem o cenário, pois no meu dominio a situaçao é justamente essa que você citou do agregado. Eles pertencem ao Usuario, efetivamente. Não há sentido no meu modelo ter um Telefone que nao pertença a um usuário, e nem um Endereço que não pertença a um usuário. Crio-os em conjunto, e removo-os em conjunto também (e também o usuário de fato só tem UM telefone e um endereço, entendo o que disse mas no meu domínio não faz sentido ter qualificadores pra esses caras).

Devido a esses detalhes que quis fazer o Usuario encapsular esses objetos, e não um “setEndereco” ou “setTelefone”. Dado esse cenário você ainda acha melhor fazer os setters?

Obrigado!

[/quote]

Você está incorrendo um erro que é muito comum, está confundinco "Ser composto de " com “Pertencer a”.

Em OO estes dois conceitos são implementados da mesma forma, com um private do tipo em questão.
quando vc diz que Endereço pertence a Usuario , vc quer dizer que Usuario tem um private do tipo Endereço.

Só que “Pertencer A” é uma relação mais poderosa que “Ser composto de”. Por exemplo, listas carrega vários objetos. Elas são compostas de objetos, mas os objetos não pertencem à lista. Ou seja, não é a Lista que sabe como, quando e porque um objeto dentro dela é criado. Não. É apenas um arranjo de objetos. Assim como um array ou qualquer tipo de estrutura dessas. Itens e Pedido é outra coisa. Os itens pertencem ao Produto. E isto não é porque o Pedido tem uma Lista de Itens é porque realmente não é possivel ter um item fora do pedido. Se vc tiver o objeto de item, a primeira coisa que vai querer saber é “Qual é o pedido onde este item está”. Mas o item diz respeito a um produto. O Produto não pertence ao Item. Então em java o produto é um private do item, e o item está em uma lista que é private do pedido, mas o item pertence ao pedido, o produto não pertece ao item.
Deu para entender ?

Da mesma forma, o private no objeto vai se trasnformar num campo em alguma tabela. A mesma coisa. O fato da tabela ter uma referencia à outra, não cria um vinculo “pertence a”, apenas cria uma relação. Nisso o banco é mais burro ainda que OO pois ele não destingue entre “é composto” ou “pertence a” ou “é relacionado com”.

Portanto tenha cuidado. quando vc diz que o Endereço só existe no Usuário isso não é verdade. Vc pode pesquisar assim “me dâs todos os endereços de todos os usuários do sistema”. Quando a lista de endereços retornar é irrelevante qual o usuario a que está anexado. Isso prova que o Endereço não pertence ao usuario. Ele está composto no usuario. Coisas que pertencem ao usuário são o nome, a senha, o email , coisas que são realmente dele e de mais ninguem.

Em UML vc representa a linha de Usuario para Endereço com um losanglo branco, não o preto. Mas pedido e item vc representa com o preto. A diferença é que o “new” de Endereço é dado no sistema, não no objeto usuário. E o “new” de pedido é dado no pedido, não no sistema.

Em resumo, duplicar todos os get/set da entidade X na entidade Y é errado. Seja qual for a relação entre elas. SE ha composição, então Y é criado pelo sistema e passado a X num set (ou no construtor). Se é algo mais complicado, outras construções podem ser necessáriaos (como o uso do padrão Builder).

O que eu queria que ficasse claro é que , não é porque ha um private que isso signfiica que ha uma relação “Pertence A”. As relações de composição ou “pertence a” vêm do mundo real. O modelo pode escolher ignorar o mundo real, mas normalmente isso é arranjar problemas porque quando vc for usar os objetos as pessoas pensam em termos do mundo real. Tipo, o outro programador que for usar API está à espera que existe uma classe Endereço se o sistema lida com endereços. Por quê ? Porque endereço é um conceito em si mesmo. O sistema pode pegar esse endereço e mandar para uma API dos correios para postagem ou para uma API de mapas para localização, para uma API de routing de entregas, etc… O endereço vive sozinho no mundo, então o programador espera um objeto sozinho. Se vc obrigar o cara a usar um objeto Usuario , com os get/set de endereço a primeira coisa que o programador vai fazer é criar uma interface Endereço e fazer usuario impleentá-la. fica uma merda, mas ele vai poder usar apenas o lhe interessa. Ou o cara vai fazer um de-para de usuario para endereço. Não tente a sorte e dê ao programador o que ele espera. E ele sempre espera o que o mundo real tem ( a isso se chama Principio da Minima Surpresa). Não surpreenda os seus colegas com designs loucos, apresente o que eles esperam.

Oi sergiotaborda, obrigado novamente. Ia postar aqui, pois refleti um pouco sobre o que você havia dito na sua primeira sugestão, e fez mais sentido do que eu quis admitir, hehe. Eu concluí o que você terminou por explicar no seu post,

A minha modelagem que estava (muito) errada, Telefone e Endereco são entidades fortes…o que eu queria fazer tinha boas intenções :lol: mas não fazia sentido.

Sobre isso que escreveu

A sua opinião é que a Lei de Demeter é desnecessária e o que o melhor é expor os atributos de composição diretamente? Dessa forma o código não incorreria em cadeias de invocações de método (tipo obj.getProp().getProp().getProp()…) ? Quando você considera que é melhor não expor um atributo que é composição?

Novamente muito obrigado! :stuck_out_tongue:

[quote=alias]Sobre isso que escreveu

A sua opinião é que a Lei de Demeter é desnecessária e o que o melhor é expor os atributos de composição diretamente? Dessa forma o código não incorreria em cadeias de invocações de método (tipo obj.getProp().getProp().getProp()…) ? Quando você considera que é melhor não expor um atributo que é composição?
[/quote]

Esse negócio de obj.getProp().getProp().getProp(). chama-se acesso telescópico. Ora, se vc faz usuario.getEndereço() isso não é um acesso telescópico. Se vc fizer usuario.getEndereco().getCEP() já é. Mas isto representa que o seu método recebe um usuário em vez de um endereço. Se o método recebe o endereço então fica endereço.getCEP() que já não é telescopico.

O acesso telescopico é ruim, a lei de Demeter fala isso, mas isso é apenas um sinal de que o seu design está errado. Que vc está tentando usar objetos muito “lá em cima” e que precisa criar métodos melhores que usem os tipos certos como entrada. Aliás porque isso vai aumentar a utilidade deles. Ou seja, tem que haver um balanço no seu design, e as regras e leis existem para lhe dizer onde vc foi longe de mais. São fronteiras. Isto serve para vc repensar seu problema e re-desenhar de outra forma. Muito provavelmente vc não entendeu o problema e por isso começou a criar coisas à toa sem muito design: o famoso “mas assim funciona”. Quando vc desenha com base em principios vc sabe quando vc está indo bem e quando vc passou da linha. Passar da linha às vezes é necessários por causa de outros constrangimentos, mas o mau não é passar da linha, o mau é não saber onde a linha está. Um certo senso cirtico é necessário porque as regras não são tão escritas na pedra assim.

Vc conhece a lei de Demeter (tb conhecida como Principio do Minimo Conhecimento) e está tentando se ater a ela e isso é fantástico, não sei que percentagem dos gujeiros sabem dela e tentam se guiar por ela. Mas também é preciso entender o core da lei, a razão porque ela foi criada. No fim é tudo uma questão de encapsulamento, diminuição da superficie da sua aplicação e desaoplamento. hoje em dias as pessoas falam muito que é preciso desacoplar e que é ruim acoplar, mas não têm muita noção ou coragem para realmente desacoplar as coisas. E às vezes, quando tentam, acabam fazendo pior. tudo porque a base de princípios não é SOLIDa :lol:
Pior que isso, as pessoas simplesmente ignoram que deveriam seguir certos princípios e que isso lhes iria facilitar a vida , e muito.

Obrigado caro sergiotaborda, ajudou muito a esclarecer alguns pensamentos por aqui.

Como disse, a minha intenção era boa inicialmente, hehe, como você compreendeu. Queria me ater à lei de Demeter, dentro do meu conhecimento dela, que se mostrou imperfeito. Ao menos posso dizer que tive a auto critica de ver que o caminho não era esse. :oops:

Novamente obrigado!