Lei de Demétrio e Agregação

Estava lendo sobre a lei de demétrio que fala sobre os objetos não podem se comunicar com estranhos.
ex:


class Funcionario
{
   private Departamento departamento;
   // getters and setters
}

class Departamento 
{
   private Filial filial;
   // getters and setters
}

class Filial
{
   private String nome;
   // getters and setters
}

String nomeFilial = funconario.getDepartamento().getFilial().getNome();

Pela essa lei isso é uma má prática. Seria um método frágil.
Porque frágil ?
Seria ideal isso:

funcionario.getNomeFilial()

Agora esse padrão não tira a reusabilidade, ou seja, não criarei redundancias e a cada método adicionado na filial terei que adicionar um método no funcionario ?
Imagina se houver um nível de agregação extremamente cascateado ?
Gostaria da opinião de vcs.

Também li algumas dicussões sobre a teoria do cara.

Concordo com você que para aplicações crud bestas (a maioria), que pegam os dados e mostram para o usuário, realmente me parece exagero aplicar a teoria.

Mas cara, brincando de fazer uns joguinhos aqui … esse lance de getA().getB().getC().fazCoisa() dói. Falta separação de responsabilidades e delegação uma hora faz a coisa desmoronar.

Lembre-se da discussão sobre getters e setters. Mandar o objeto fazer o que você precisa feito é melhor que pegar os dados para fazer você mesmo.

Isso que eu não entendi, por que faz a coisa desmoronar ?
Separando as responsabilidades vc tira as reduncias.
Isso é vital para ter “uma representacao relacional em OO”.

Ja fica menos pior assim:

Deve ser chato, mas poderia-se usar interfaces (funcionario implements filial). Assim,

Internamente seria

Espero nunca pasar por isso.

A ideia da interface é boa, mas esse ao meu ver é o problema.
Deixar somente os atributos (métodos acessores) da filial na filial é o interessante.

Imagina se eu tiver 100 classes que tem como atributos o departamento e precisar obter o nome da filial em diversos lugares.
Imagina em cada métodos adicionado na filial tiver que implementar a interface filial em cada dessas classes.

Eu acredito que o pessoal tá fazendo igual a primeira maneira sem problemas.
O que eu estou discutindo é a colocação desse Demeter em falar que é uma maneira frágil.
Gostaria de entender…

A lei de Deméter (que era o nome do sistema desenvolvido quando propuseram esta lei)é uma rule of thumb. Ela não explica o porque (apesar dos papers tentarem), mas ela diz: não faça.

Na verdade, se você tem que navegar muito nos seus objetos, e pode ser por um:

String nomeFilial = funconario.getDepartamento().getFilial().getNome();

ou um:

String nomeFilial = null;
Departamento departamento = null;
Filial filial=null;

departamento=funconario.getDepartamento();

filial=departamento.getFilial();

nome=filialgetNome();

tanto faz, você está, provavelmente, com um problema na sua modelagem. Pode até ser que você queira e precise pegar o objeto no grafo, mas provavelment eo que você teria que fazer era delegar isso para uma dessas classes de meio de campo.

Shoes se possível dá um exemplo.
O problema de fazer diferente é devido o mapeamento OOxRelacional.
Esse é grande problema.
Você tira a “normalização” das classes.

jprogrammer, no seu exemplo você está simplesmente chamando um monte de getters feios para pegar o valor de uma String.
Mas imagine que os objetos são algo mais complexo que pojos. Não te cheira a peixe o Command/Action/etc precisar passar por todo esse caminho para realizar uma tarefa?

class Action {
    void execute() {
        paint( universe.getCurrentHouse().getFloor().getGraphics() );
        paint( universe.getCurrentHouse().getObjects().getGraphics() );
        paint( universe.getCurrentHouse().getWalls().getGraphics() );
        paint( universe.getCurrentHouse().getWindows().getGraphics() );
        paint( universe.getCurrentHouse().getNpcs().getGraphics() );
    }
}

Olha que coisa nojenta.

Ficar bonito não fica, mas e o problema do mapeamento relacional.

Este é um dos casos quando você quer e precisa pegar objetos no grafo, mas você não deve basear sua lógica de negócio nessa interface.

Faça aquele esquema Factory/Implementação/Interface :wink:

Mas isso não tira a “normalização” das classes.
Não ficaria estranho isso:


class Funcionario
{
   public String getNomeDepartamento() {}
   public String getNomeFilial() {}
   public String getMediaSalarioDepartamento() {}
   public String getChefeDepartamento() {}
  // assim vai....
}

Imagina as inúmeros métodos que tenho que criar conforme for agregando objetos a funcionario e adicionando métodos as classes agragadas.
Imagine num nível de agregação gigantesco…

Tenho a impressão de não estar entendendo alguma coisa do seu problema…
Parece que vc tem um problema de atribuição de responsabilidades no seu projeto. Não me parece razoável que a classe Funcionário tenha a responsabilidade de conhecer a média salarial do departamento. Vc poderia me dizer para que é exatamente que vc precisa disso em funcionário? Talvez a gente possa ajudar a projetar uma solução melhor com um exemplo concreto.
Como disse o LIPE, o melhor é deixar para quem já tem a informação necessária fazer o que é preciso.
O objeto tem que ter gets/sets somente para os seus atributos normais, e não para os atributos dos seus atributos…
Será que estou falando besteira??

Não tenho problema nenhum.
É apenas conceitual.

Pela lei de Deméter fazer muiitas chamadas é errado.
Apenas esto querndo entender o porquê (se tem).

Muitos getters é sinal de peixe pobre.

O Eclipse, por exemplo, tem os refactoring necessarios para resolver isso. Como exercício, pegue um método cheio disso e faça o seguinte:

1)mova o método para a classe com maior número de getters, setter e métodos invocados.
2)agrupe as declarações de acordo com os tipos envolvidos.
3)extraia métodos menores de acordo com os clusters encontrados no passo 2
4)repita passos 1 a 3 ate não existirem mais getters/setters sendo chamados
5)faça inline dos métodos que são simples demais
6)mova os atributos que tem getters/setters para tipos mais próximos dos que chamam eles
7)repita 1 a 6 até exaustão.

O bonus round é no dia seguinte, compare o original com o resultado e analise se o código melhou ou não.

Se você se sentir realmente desafiado, tem um passo extra entre troque o passo 7 pelos seguintes:

7)garanta que existem menos métodos retornando valores (!= void) que na iteração anterior
8 )repita 1 a 7 até não existirem mais métodos retornando valores ou desmaiar por exaustão

Se isso não fizer você enxergar OO como o Neo vê a matrix, desista.

javinha, você não falou besteira, mas tá no contexto errado :wink:

jprogrammer (esses nicks de vocês estão me deixando louco!): sim. Lei de Deméter é como (na verdade, é muito relacionada com) acoplamento: você não vai (e talvez não deveria) se livrar totalmente.

A questão é: será que a rotina que precisa do nome do depto precisa navegar partindo do funcionario? Se sim, ok, vc vai rpecisar fazer isso, se não, você pdoe criar um meio menos acoplado.

louds tem como exempleficar isso com o exemplo.

antes:

class Action {
    void execute() {
        paint( universe.getCurrentHouse().getFloor().getGraphics() );
        paint( universe.getCurrentHouse().getObjects().getGraphics() );
        paint( universe.getCurrentHouse().getWalls().getGraphics() );
        paint( universe.getCurrentHouse().getWindows().getGraphics() );
        paint( universe.getCurrentHouse().getNpcs().getGraphics() );
    }
}
class Action {
    void execute() {
        universe.paint();
    }
}

class Universe {
    void paint() {
          currentHouse.paint();  
    }
}

class House {
    void paint() {
        floor.paint();

        for( MyObject obj : objs )
            obj.paint();
        for( Wall wall : walls )
            wall.paint();
        for( Window win : windows )
            win.paint();
        for( Npc npc : npcs )
            npc.paint();
    }
}

Isso por que é só um método bestinha. Nesse caso é aplicada o pattern Composite (de um jeito porquinho, o melhor seria usar uma interface Paintable), mas poderia ser exetendido para coisas mais complexas.

O problema não é fazer a ação.
O problema é exibir os valor para o mundo externo.

Para uma view por exemplo.

MVC… Sua View instala listeners no Model e quando for notificada de modificações altera os dados exibidos para o usuario.

Mas ai não geraria acoplamento…