Há o problema clássico de POO do encadeamento de métodos versus métodos de "ponto de acesso único":
main.getA().getB().getC().transmogrify(x, y)
vs
main.getA().transmogrifyMyC(x, y)
A primeira parece ter a vantagem de que cada classe é responsável apenas por um conjunto menor de operações e torna tudo muito mais modular - adicionar um método a C não requer nenhum esforço em A, B ou C para expô-lo.
A desvantagem, é claro, é o encapsulamento mais fraco , que o segundo código resolve. Agora, A tem controle de todos os métodos que passam por ele e pode delegá-lo a seus campos, se desejar.
Percebo que não há uma solução única e, é claro, depende do contexto, mas eu realmente gostaria de ouvir algumas opiniões sobre outras diferenças importantes entre os dois estilos e sob quais circunstâncias devo preferir um deles - porque agora, quando tento Para projetar algum código, sinto que não estou usando os argumentos para decidir de uma maneira ou de outra.
fonte
Em geral, tento manter o método encadeado o mais limitado possível (com base na Lei de Demeter )
A única exceção que faço é para interfaces fluentes / programação interna no estilo DSL.
Martin Fowler faz a mesma distinção em Idiomas Específicos de Domínio, mas por razões de violação da separação de consultas de comando, que afirma:
Fowler em seu livro na página 70 diz:
fonte
Penso que a questão é se você está usando uma abstração adequada.
No primeiro caso, temos
Onde principal é do tipo
IHasGetA
. A questão é: essa abstração é adequada. A resposta não é trivial. E, neste caso, parece um pouco estranho, mas é um exemplo teórico de qualquer maneira. Mas para construir um exemplo diferente:Geralmente é melhor que
Porque no último exemplo ambos
this
emain
dependem dos tipos dev
,w
,x
,y
ez
. Além disso, o código realmente não parece muito melhor, se toda declaração de método tiver meia dúzia de argumentos.Um localizador de serviço realmente requer a primeira abordagem. Você não deseja acessar a instância criada por meio do localizador de serviço.
Portanto, "alcançar" através de um objeto pode criar muita dependência, ainda mais se for baseado nas propriedades das classes reais.
No entanto, criar uma abstração, que consiste em fornecer um objeto, é uma coisa totalmente diferente.
Por exemplo, você poderia ter:
Onde
main
é uma instância deMain
. Se o conhecimento da classemain
reduz a dependência para, emIHasGetA
vez deMain
, você encontrará o acoplamento realmente muito baixo. O código de chamada nem sabe que está realmente chamando o último método no objeto original, o que ilustra o grau de dissociação.Você alcança um caminho de abstrações concisas e ortogonais, em vez de se aprofundar nas partes internas de uma implementação.
fonte
A Lei de Demeter , como aponta Péter Török, sugere a forma "compacta".
Além disso, quanto mais métodos você mencionar explicitamente em seu código, mais classes elas dependem, aumentando os problemas de manutenção. No seu exemplo, o formato compacto depende de duas classes, enquanto o formato mais longo depende de quatro classes. A forma mais longa não só viola a Lei de Demeter; isso também fará com que você altere seu código toda vez que alterar qualquer um dos quatro métodos referenciados (em oposição a dois no formato compacto).
fonte
A
explodirá com muitos métodos queA
podem delegar de qualquer maneira. Ainda assim, eu concordo com as dependências - isso reduz drasticamente a quantidade de dependências necessárias do código do cliente.Eu mesmo lutei com esse problema. A desvantagem de "alcançar" profundamente objetos diferentes é que, quando você refatorar, terá que alterar uma enorme quantidade de código, pois existem muitas dependências. Além disso, seu código se torna um pouco inchado e mais difícil de ler.
Por outro lado, ter classes que simplesmente "transmitem" métodos também significa uma sobrecarga de ter que declarar vários métodos em mais de um local.
Uma solução que atenua isso e é apropriada em alguns casos é ter uma classe de fábrica que cria um tipo de objeto de fachada, copiando dados / objetos das classes apropriadas. Dessa forma, você pode codificar no objeto de fachada e, quando refatorar, simplesmente altera a lógica da fábrica.
fonte
Costumo achar que é mais fácil entender a lógica de um programa com métodos encadeados. Para mim,
customer.getLastInvoice().itemCount()
se encaixa melhor no meu cérebrocustomer.countLastInvoiceItems()
.Vale a pena a dor de cabeça na manutenção de ter o acoplamento extra. (Também gosto de funções pequenas em turmas pequenas, por isso costumo encadear. Não estou dizendo que está certo - é exatamente o que faço.)
fonte