Suponha que temos um módulo de software A que implementa uma função F. Outro módulo B implementa a mesma função que F '.
Existem várias maneiras de se livrar do código duplicado:
- Deixe A usar F 'de B.
- Deixe B usar F de A.
- Coloque F em seu próprio módulo C e deixe que A e B o usem.
Todas essas opções geram dependências adicionais entre os módulos. Eles aplicam o princípio DRY ao custo de aumentar o acoplamento.
Tanto quanto posso ver, o acoplamento é sempre aumentado ou, quando alugado, passa para um nível mais alto ao aplicar o DRY. Parece haver um conflito entre dois dos princípios mais básicos do design de software.
(Na verdade, não acho surpreendente a existência de conflitos como esse. Provavelmente é isso que torna o bom design de software tão difícil. Acho surpreendente que esses conflitos normalmente não sejam abordados em textos introdutórios.)
Editar (para esclarecimento): Presumo que a igualdade de F e F 'não seja apenas uma coincidência. Se F precisar ser modificado, F 'provavelmente precisará ser modificado da mesma maneira.
fonte
Respostas:
Por que sim eles fazem. Mas eles diminuem o acoplamento entre as linhas. O que você obtém é o poder de alterar o acoplamento. O acoplamento vem de várias formas. A extração de código aumenta a indireção e a abstração. Aumentar isso pode ser bom ou ruim. A primeira coisa que decide qual você recebe é o nome que você usa para isso. Se olhar para o nome me surpreende quando olho para dentro, então você não fez nenhum favor a ninguém.
Além disso, não siga DRY no vácuo. Se você eliminar a duplicação, estará assumindo a responsabilidade de prever que esses dois usos desse código mudarão juntos. Se é provável que eles mudem independentemente, você causou confusão e trabalho extra com pouco benefício. Mas um nome realmente bom pode tornar isso mais agradável. Se tudo o que você pode pensar é um nome ruim, por favor, pare agora.
O acoplamento sempre existirá, a menos que seu sistema seja tão isolado que ninguém nunca saiba se funciona. Portanto, refatorar o acoplamento é um jogo de escolha do seu veneno. A seguir, o DRY pode render, minimizando o acoplamento criado, expressando a mesma decisão de projeto repetidamente em muitos lugares, até que seja muito difícil mudar. Mas o DRY pode impossibilitar a compreensão do seu código. A melhor maneira de salvar essa situação é encontrar um nome realmente bom. Se você não consegue pensar em um bom nome, espero que você seja capaz de evitar nomes sem sentido.
fonte
Existem maneiras de quebrar dependências explícitas. Um dos mais populares é injetar dependências em tempo de execução. Dessa forma, você obtém DRY e remove o acoplamento a custo de segurança estática. Hoje em dia, é tão popular que as pessoas nem sequer entendem que isso é uma troca. Por exemplo, os contêineres de aplicativos rotineiramente fornecem gerenciamento de dependência que complica imensamente o software, ocultando a complexidade. Mesmo a injeção simples de construtor antigo falha em garantir alguns contratos devido à falta do sistema de tipos.
Para responder ao título - sim, é possível, mas esteja preparado para as consequências do envio em tempo de execução.
Dessa forma, o único tipo de dependência que você teria é D, dependendo do outro módulo.
Ou registre C no contêiner do aplicativo com injeção de dependência integrada e aproveite a sorte de cabeamento automático que cresce lentamente loops e bloqueios de carregamento de classe em tempo de execução.
fonte
Não tenho certeza de que uma resposta sem outro contexto faça sentido.
Será que
A
já dependemB
ou vice-versa? - nesse caso, poderíamos ter uma escolha óbvia de casaF
.Não
A
eB
já compartilham quaisquer dependências comuns que podem ser um bom lar paraF
?Quão grande / complexo é
F
? Do que maisF
depende?São módulos
A
eB
usados no mesmo projeto?Will
A
eB
acabam compartilhando alguns de qualquer maneira dependência comum?Que sistema de linguagem / módulo está sendo usado: Quão doloroso é um novo módulo, na dor do programador, na sobrecarga de desempenho? Por exemplo, se você estiver escrevendo em C / C ++ com o sistema de módulo COM, o que causa problemas no código fonte, requer ferramentas alternativas, tem implicações na depuração e tem implicações no desempenho (para invocações entre módulos), talvez faça uma pausa séria.
Por outro lado, se você está falando sobre DLLs Java ou C # que combinam perfeitamente em um único ambiente de execução, isso é outra questão.
Uma função é uma abstração e suporta DRY.
No entanto, boas abstrações precisam ser concluídas - abstrações incompletas podem muito bem fazer o cliente consumidor (programador) compensar o déficit usando o conhecimento da implementação subjacente: isso resulta em um acoplamento mais rígido do que se a abstração fosse oferecida como mais completa.
Então, eu argumentaria procurar criar uma abstração melhor
A
e comB
a qual depender do que simplesmente mover uma única função para um novo móduloC
.Eu estaria procurando por um conjunto de funções para provocar uma nova abstração, ou seja, eu poderia esperar até que a base de código fosse adiante para identificar uma refatoração de abstração mais completa / mais completa a ser executada em vez de uma baseada em um único código de função informe.
fonte
Does A already depend on B or vice versa? — in which case we might have an obvious choice of home for F.
Isso pressupõe que A sempre confiará em B (ou vice-versa), que é uma suposição muito perigosa. O fato de OP ver F como inerentemente não faz parte de A (ou B) sugere que F existe sem ser inerente a nenhuma biblioteca. Se F pertence a uma biblioteca (por exemplo, um método de extensão DbContext (F) e uma biblioteca de wrapper do Entity Framework (A ou B)), a pergunta do OP seria discutível.As respostas aqui, focadas em todas as maneiras pelas quais você pode "minimizar" esse problema, estão fazendo um desserviço. E “soluções” simplesmente oferecendo maneiras diferentes de criar acoplamentos não são realmente soluções.
A verdade é que não estão conseguindo entender o mesmo problema que você criou. O problema do seu exemplo não tem nada a ver com DRY, e sim (mais amplamente) com o design do aplicativo.
Pergunte-se por que os módulos A e B são separados se ambos dependem da mesma função F? É claro que você terá problemas com o gerenciamento de dependências / abstração / acoplamento / nome-do-it se você se comprometer com um design inadequado.
A modelagem de aplicativo adequada é feita de acordo com o comportamento. Como tal, as partes de A e B que são dependentes de F precisam ser extraídas em seu próprio módulo independente. Se isso não for possível, A e B precisam ser combinados. Nos dois casos, A e B não são mais úteis para o sistema e devem deixar de existir.
O DRY é um princípio que pode ser usado para expor um design inadequado, não para causar isso. Se você não conseguir obter DRY ( quando realmente se aplica - observando sua edição) devido à estrutura do seu aplicativo, é um sinal claro de que a estrutura se tornou um passivo. É por isso que a refatoração contínua também é um princípio a seguir.
Do de outros diretores de design (sólida, seca, etc) estão lá fora, ABC todos usados para fazer a mudança (incluindo refactoring) uma aplicação mais indolor. Concentre-se nisso e todos os outros problemas começam a desaparecer.
fonte
Eu tenho uma opinião diferente, pelo menos para a terceira opção:
Da sua descrição:
Colocar F em um módulo C não aumenta o acoplamento, pois A e B já precisam do recurso de C.
fonte