Lei de Demétrio e Agregação

21 respostas
J

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.

21 Respostas

_

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.

J

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”.

R

Ja fica menos pior assim:

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

Internamente seria

Espero nunca pasar por isso.

J

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…

P

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.

J

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.

J

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

P

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:

J

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…

J

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??

J

Não tenho problema nenhum.
É apenas conceitual.

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

L

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.

P

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.

J

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.

J

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

Para uma view por exemplo.

L

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

J

Mas ai não geraria acoplamento…

_

Nesse caso ou você usa a idéia do Louds ou a idéia daquele artigo.

L

jprogrammer:
Mas ai não geraria acoplamento…

Exatamente o contrario. Diminui o acoplamento se você tiver o Controller fazendo o plumbing.

Criado 6 de maio de 2005
Ultima resposta 6 de mai. de 2005
Respostas 21
Participantes 6