Eu tenho três perguntas sobre a lei de Deméter.
Além das classes que foram especificamente designadas para retornar objetos - como as classes factory e builder -, é aceitável que um método retorne um objeto, por exemplo, um objeto mantido por uma das propriedades da classe ou que viole a lei do demeter (1) ? E se viola a lei do demeter, importaria se o objeto retornado fosse um objeto imutável que represente um dado e não contenha nada além de getters para esses dados (2a)? Ou esse ValueObject é um antipadrão, porque tudo o que é feito com os dados dessa classe é feito fora da classe (2b)?
No pseudo-código:
class A {}
class B {
private A a;
public A getA() {
return this.a;
}
}
class C {
private B b;
private X x;
public void main() {
// Is it okay for B.getA to exist?
A a = this.b.getA();
a.doSomething();
x.doSomethingElse(a);
}
}
Suspeito que a lei de Deméter proíba um padrão como o acima. O que posso fazer para garantir que isso doSomethingElse()
possa ser chamado sem violar a lei (3)?
fonte
x
sendo definido?x
é apenas uma propriedade diferente cujo valor é apenas um objeto que é capaz de invocardoSomethingElse
.user.currentSession.isLoggedIn()
. porque une clientesuser
não apenasuser
mas também seu colaboradorsession
,. Em vez disso, você deseja escreveruser.isLoggedIn()
. isso geralmente é alcançado adicionando umisLoggedIn
método aouser
qual a implementação delegacurrentSession
.Respostas:
Em muitas linguagens de programação, todos os valores retornados são objetos. Como outros já disseram, não poder usar os métodos de objetos retornados obriga a nunca retornar nada. Você deve se perguntar: "Quais são as responsabilidades das classes A, B e C?" É por isso que o uso de nomes de variáveis metassintáticas como A, B e C sempre solicita a resposta "depende" porque não há responsabilidades herdadas nesses termos.
Você pode querer examinar o Design Orientado a Domínio, que fornecerá um conjunto mais detalhado de heurísticas para refletir sobre onde a funcionalidade deve ir e quem deve estar chamando o quê.
Sua segunda pergunta sobre objetos imutáveis fala da noção de um
objeto de valor comparado a um objeto de entidade. no DDD, você pode transmitir objetos de valor quase sem restrições. Isso não é verdadeiro para objetos de entidade.
O LOD simplista é muito melhor expresso em DDD como as regras para acessar agregados . Nenhum objeto externo deve conter referências a membros de agregados. Somente uma referência raiz deve ser mantida.
Além do DDD, você pelo menos deseja desenvolver seus próprios conjuntos de estereótipos para as classes que são razoáveis para o seu domínio. Aplique regras sobre esses estereótipos ao criar seu sistema.
Lembre-se também sempre de que todas essas regras são para gerenciar a complexidade, não para prejudicar a si mesmo.
fonte
Law of Demeter
,tell, don't ask
,getters are evil
,object encapsulation
esingle responsibility principle
parecem resumir-se a esta pergunta: quando deve delegação para um objeto de domínio ser usado ao contrário de começar o valor do objeto e fazer algo com ele? Seria muito útil poder aplicar qualificações diferenciadas a situações em que essa decisão deve ser tomada.Sim, certamente é.
Vejamos os principais pontos :
Todos os três permitem que você faça uma pergunta: Quem é um amigo?
Ao decidir o que retornar, a Lei de Demeter ou o princípio do mínimo conhecimento (LoD) não determina que você se defenda contra codificadores que insistem em violá-lo. Ele determina que você não força os codificadores a violá-lo.
Confundir isso é exatamente o motivo pelo qual muitos acham que um levantador deve sempre retornar vazio. Não. Você deve permitir uma maneira de fazer consultas (getters) que não alterem o estado do sistema. Essa é a separação básica da consulta de comando .
Isso significa que você é livre para se aprofundar em uma base de código encadeando o que quiser? Não. Encadeie apenas o que deveria ser encadeado. Caso contrário, a corrente poderá mudar e, de repente, seu material será quebrado. É isso que os amigos querem dizer.
Podem ser projetadas cadeias longas. Interfaces fluentes, iDSL, grande parte do Java8 e o bom e antigo StringBuilder destinam-se a permitir que você construa longas cadeias. Eles não violam o LoD porque tudo na cadeia deve trabalhar juntos e promete continuar trabalhando juntos. Você viola o desmonte quando encadeia coisas que nunca ouviram falar um do outro. Amigos são aqueles que prometeram manter sua rede funcionando. Amigos de amigos não.
Isso cria apenas uma oportunidade de violar o demeter. Isto não é uma violação. Isso nem é necessariamente ruim.
Imutável é bom, mas irrelevante aqui. Chegar às coisas através de uma cadeia mais longa não a torna melhor. O que o torna melhor é separar a obtenção do uso. Se você estiver usando, peça o que você precisa como parâmetro. Não vá procurá-lo, mergulhando nos caçadores de estranhos.
Antes de falar sobre
x.doSomethingElse(a)
entender que você escreveu basicamenteb.getA().doSomething()
Agora, LoD não é um exercício de contagem de pontos . Mas quando você cria uma cadeia, está dizendo que sabe como obter um
A
(usandoB
) e sabe como usar umA
. Bem agoraA
eB
melhor ter amigos íntimos, porque você os juntou.Se você tivesse acabado de pedir algo para lhe entregar uma
A
coisa semelhante, você poderia usarA
e não se importaria de onde veio eB
poderia viver uma vida feliz e livre de sua obsessão por sairA
deB
.Quanto
x.doSomethingElse(a)
sem detalhes de ondex
veio, o LoD não tem nada a dizer sobre isso.O LoD pode incentivar a separação do uso da construção . Mas vou apontar que se você tratar religiosamente todos os objetos como não amigáveis, ficará preso escrevendo código em métodos estáticos. Você pode construir um gráfico de objeto maravilhosamente complexo dessa maneira, mas eventualmente precisará chamar um método para começar a trabalhar. Você simplesmente tem que decidir quem são seus amigos. Não há como fugir disso.
Então, sim, uma classe tem permissão para retornar um de seus membros sob LoD. Ao fazê-lo, você deve deixar claro se esse membro é amigável com sua classe, porque, se não for, algum cliente pode tentar acoplá-lo a essa classe usando você para obtê-la antes de usá-la. Isso é importante porque agora o que você retorna sempre deve suportar esse uso.
Existem muitos casos em que isso simplesmente não é uma preocupação. As coleções podem ignorar isso simplesmente porque são feitas para serem amigas de tudo que as usa. Da mesma forma, objetos de valor são amigáveis com todos. Mas se você escreveu um utilitário de validação de endereço que exigia um objeto de funcionário do qual ele extraiu um endereço, em vez de apenas solicitar o endereço, é melhor esperar que o funcionário e o endereço sejam da mesma biblioteca.
fonte
settings.darkTheme().monospaceFont()
é apenas açúcar sintático parasettings.darkTheme(); settings.monospaceFont();
Não entendo bem esse argumento. Qualquer objeto pode retornar um objeto como resultado de uma chamada de mensagem. Especialmente se você pensa em "tudo como objeto".
As classes, no entanto, são os únicos objetos que podem instanciar novos objetos de seu próprio tipo, enviando a eles a mensagem "nova" (ou frequentemente substituída por uma nova palavra-chave nos idiomas OOP mais comuns)
De qualquer forma, em relação à sua pergunta, eu suspeitaria que um objeto retornasse uma de suas propriedades para ser usado em outro lugar. Pode ser que esse objeto esteja vazando informações sobre seu estado ou detalhes de implementação.
E você não gostaria que o objeto C soubesse sobre os detalhes de implementação de B. Essa é uma dependência indesejada do objeto A da perspectiva do objeto C.
Portanto, você pode se perguntar se C, ao conhecer A, não está sabendo muito sobre o trabalho que ele tem que fazer, independentemente do LOD.
Há casos em que isso pode ser legítimo, por exemplo, solicitar a um objeto de coleção um de seus itens é apropriado e não violar nenhuma regra Demeter. Pedir um objeto Book para seu objeto SQLConnection, por outro lado, é arriscado e deve ser evitado.
O LOD existe porque muitos programadores tendem a solicitar informações aos objetos antes de realizar um trabalho. O LOD existe para nos lembrar que, às vezes (se não sempre), estamos complicando demais as coisas, desejando que nossos objetos façam muito. Às vezes, apenas precisamos pedir a outro objeto que faça o trabalho por nós. O LOD existe para nos fazer pensar duas vezes sobre onde colocamos o comportamento em nossos objetos.
fonte
Por que não seria? Você parece sugerir que é necessário sinalizar para seus colegas programadores que a classe é especificamente destinada a retornar objetos, ou ela não deve retornar objetos. Nos idiomas em que tudo é um objeto, isso limitaria severamente suas opções, pois você não seria capaz de retornar nada.
fonte
b.getA().doSomething()
ex.doSomethingElse(b.getA())
A a = b.getA(); a.DoSomething();
- de volta a um ponto.b.getA().doSomething()
ex.doSomethingElse(b.getA())
deveria ter perguntado explicitamente sobre isso. Tal como está, sua pergunta parece ser sobrethis.b.getA()
.A
não é um membro deC
, não criado emC
e não fornecido paraC
, parece que o usoA
emC
(de qualquer maneira) viola LoD. Contar pontos não é o objetivo do LoD, é apenas uma ferramenta ilustrativa. É possível converterDriver().getCar().getSteeringWheel().getFrontWheels().toRight()
em instruções separadas, cada uma contendo um ponto, mas a violação do LoD ainda está lá. Eu li em muitos posts neste fórum que a execução de ações deve ser delegada ao objeto que implementa o comportamento real, ou sejatell don't ask
. Retornar valores parece entrar em conflito com isso.Depende do que você está fazendo. Se você expõe um membro da sua classe, quando não é da conta de ninguém que ele seja membro da sua classe ou qual é o tipo desse membro, isso é uma coisa ruim. Se você alterar o tipo desse membro e o tipo de função que está retornando o membro, e todo mundo precisar alterar seu código, isso é mau.
Por outro lado, se a interface da sua classe declarar que ela fornecerá uma instância de uma conhecida classe A, tudo bem. Se você tiver sorte e puder fazer isso, forneça um membro da sua classe, que bom para você. Se você alterou esse membro de A para B, seria necessário reescrever o método que retorna A, obviamente ele precisará construir um e preenchê-lo com os dados disponíveis, em vez de retornar apenas uma linha um membro. A interface da sua classe não seria alterada.
fonte