Eu trabalho em uma base de código bastante grande. Centenas de classes, toneladas de arquivos diferentes, muitas funcionalidades, leva mais de 15 minutos para baixar uma cópia nova, etc.
Um grande problema com uma base de código tão grande é que ela possui muitos métodos utilitários e faz a mesma coisa, ou possui código que não utiliza esses métodos utilitários quando poderia. E também os métodos utilitários não são todos da mesma classe (porque seria uma grande confusão).
Sou bastante novo na base de código, mas o líder da equipe que trabalha nela há anos parece ter o mesmo problema. Isso leva a muito código e duplicação de trabalho e, como tal, quando algo quebra, geralmente é quebrado em 4 cópias basicamente do mesmo código
Como podemos refrear esse padrão? Como na maioria dos projetos grandes, nem todo o código é documentado (embora alguns o sejam) e nem todo o código é ... bem, limpo. Mas, basicamente, seria muito bom se pudéssemos trabalhar para melhorar a qualidade a esse respeito, para que, no futuro, tivéssemos menos duplicação de código e coisas como funções utilitárias fossem mais fáceis de descobrir.
Além disso, as funções utilitárias geralmente estão em alguma classe auxiliar estática, em alguma classe auxiliar não estática que funciona em um único objeto ou é um método estático na classe com a qual ela "ajuda" principalmente.
Eu tive um experimento em adicionar funções utilitárias como métodos de extensão (não precisava de nenhum elemento interno da classe, e isso definitivamente só era necessário em cenários muito específicos). Isso teve o efeito de impedir a bagunça da classe principal e tal, mas não é mais possível descobrir a menos que você já saiba sobre ela
Respostas:
A resposta simples é que você realmente não pode impedir a duplicação de código. No entanto, você pode "corrigi-lo" através de um processo incremental repetitivo contínuo difícil que se resume em duas etapas:
Etapa 1. Comece a escrever testes no código legado (de preferência usando uma estrutura de teste)
Etapa 2. Reescreva / refatorar o código duplicado usando o que aprendeu com os testes
Você pode usar ferramentas de análise estática para detectar código duplicado e, para C #, existem várias ferramentas que podem fazer isso por você:
Ferramentas como essa o ajudarão a encontrar pontos no código que faz coisas semelhantes. Continue escrevendo testes para determinar o que realmente fazem; use os mesmos testes para simplificar o uso do código duplicado. Essa "refatoração" pode ser feita de várias maneiras e você pode usar esta lista para determinar a correta:
Além disso, há também um livro inteiro sobre esse tópico, de Michael C. Feathers, Trabalhando efetivamente com o código legado . Ele detalha diferentes estratégias que você pode adotar para alterar o código para melhor. Ele tem um "algoritmo de alteração de código herdado", que não está longe do processo de duas etapas acima:
O livro é uma boa leitura, se você estiver lidando com desenvolvimento de campo integral, ou seja, código legado que precisa ser alterado.
Nesse caso
No caso do OP, posso imaginar que o código não testável é causado por um pote de mel para "métodos e truques de utilidade" que assumem várias formas:
Observe que não há nada errado com elas, mas, por outro lado, elas geralmente são difíceis de manter e mudar. Os métodos de extensões no .NET são métodos estáticos, mas também são relativamente fáceis de testar.
Antes de prosseguir com as refatorações, converse com sua equipe sobre isso. Eles precisam ser mantidos na mesma página que você antes de prosseguir com qualquer coisa. Isso ocorre porque, se você refatorar algo, as chances são altas de causar conflitos de mesclagem. Portanto, antes de refazer algo, investigue-o e peça à sua equipe para trabalhar nesses pontos de código com cautela por um tempo até terminar.
Como o OP é novo no código, há outras coisas a fazer antes de fazer qualquer coisa:
Boa sorte!
fonte
Também poderíamos tentar ver o problema de outro ângulo. Em vez de pensar que o problema é a duplicação de código, podemos considerar se o problema se origina na falta de políticas para reutilização de código.
Recentemente, li o livro Engenharia de software com componentes reutilizáveis e ele realmente tem um conjunto de idéias muito interessantes sobre como promover a reutilização de código no nível da organização.
O autor deste livro, Johannes Sametinger, descreve um conjunto de barreiras à reutilização de código, algumas conceituais e outras técnicas. Por exemplo:
Segundo o autor, diferentes níveis de reutilização acontecem dependendo da maturidade de uma organização.
Portanto, talvez, além de todas as sugestões fornecidas em outras respostas, você possa trabalhar no design de um programa de reutilização, envolver o gerenciamento, formar um grupo de componentes responsável pela identificação de componentes reutilizáveis, fazendo análises de domínio e definindo um repositório de componentes reutilizáveis que outros desenvolvedores possam facilmente consulte e procure soluções prontas para seus problemas.
fonte
Existem 2 soluções possíveis:
Prevenção - Tente ter a melhor documentação possível. Torne todas as funções adequadamente documentadas e fáceis de pesquisar em toda a documentação. Além disso, ao escrever um código, torne óbvio para onde o código deve ir; portanto, é óbvio para onde procurar. A quantidade limite de código "utilitário" é um dos pontos principais disso. Toda vez que ouço "vamos fazer aula de utilidade", meu cabelo sobe e meu sangue congela, porque é obviamente um problema. Sempre tenha uma maneira rápida e fácil de solicitar que as pessoas conheçam a base de código sempre que algum recurso já existir.
Solução - Se a prevenção falhar, você poderá resolver rápida e eficientemente a parte problemática do código. Seu processo de desenvolvimento deve permitir a rápida correção de códigos duplicados. O teste de unidade é perfeito para isso, porque você pode modificar o código com eficiência, sem medo de quebrá-lo. Portanto, se você encontrar duas partes de código semelhantes, abstraí-las para uma função ou classe deve ser fácil com um pouco de refatoração.
Pessoalmente, não acho que a prevenção seja possível. Quanto mais você tenta, mais é problemático encontrar recursos já existentes.
fonte
Eu não acho que esse tipo de problema tenha a solução geral. O código duplicado não será criado se os desenvolvedores tiverem disposição suficiente para procurar o código existente. Os desenvolvedores também podem corrigir os problemas no local, se quiserem.
Se o idioma for C / C ++, a mesclagem de duplicação será mais fácil devido à flexibilidade do vínculo (pode-se chamar qualquer
extern
função sem informações anteriores). Para Java ou .NET, pode ser necessário criar classes auxiliares e / ou componentes de utilitários.Normalmente, começo a remoção da duplicação do código existente apenas se os principais erros surgirem das partes duplicadas.
fonte
Esse é um problema típico de um projeto maior, que foi tratado por muitos programadores, que tem contribuído sob algumas vezes muita pressão dos colegas. É muito tentador fazer uma cópia de uma classe e adaptá-la a essa classe específica. No entanto, quando um problema foi encontrado na classe de origem, ele também deve ser resolvido nos seus falecidos, que geralmente são esquecidos.
Existe uma solução para isso e é chamado Generics, que foi introduzido no Java 6. É o equivalente ao C ++ chamado Template. Código do qual a classe exata ainda não é conhecida dentro de uma Classe Genérica. Por favor, verifique o Java Generics e você encontrará toneladas e toneladas de documentação para isso.
Uma boa abordagem é reescrever o código que parece ser copiado / colado em muitos lugares, reescrevendo o primeiro que você precisa, ou seja, corrigir por causa de um determinado bug. Reescreva-o para usar Genéricos e também escreva código de teste muito rigoroso.
Verifique se todos os métodos da classe Generic são invocados. Você também pode introduzir ferramentas de cobertura de código: o código genérico deve ser totalmente cobertura de código, pois será usado em vários locais.
Escreva também o código de teste, ou seja, usando JUnit ou similar para a primeira classe designada que será usada em conjunto com o trecho de código genérico.
Comece a usar o código genérico para a segunda versão (na maioria das vezes) copiada quando todo o código anterior funcionar e for totalmente testado. Você verá que existem algumas linhas de código específicas para essa classe designada. Você pode chamar essas linhas de código em um método protegido abstrato que precisa ser implementado pela classe derivada que usa a classe base Genérica.
Sim, é um trabalho tedioso, mas à medida que você avança, será cada vez melhor eliminar classes semelhantes e substituí-lo por algo que é muito, muito limpo, bem escrito e muito mais fácil de manter.
Eu tive uma situação semelhante em que na classe genérica eventualmente substituímos algo como 6 ou 7 outras classes quase idênticas que eram quase idênticas, mas que foram copiadas e coladas por vários programadores ao longo de um período de tempo.
E sim, sou muito a favor do teste automatizado do código. Custará mais no começo, mas certamente poupará uma quantidade enorme de tempo em geral. E tente obter uma cobertura geral de pelo menos 80% e 100% para o código genérico.
Espero que isso ajude e boa sorte.
fonte
Na verdade, vou ecoar a opinião menos popular aqui e
Gangnus
sugerir que a duplicação de código nem sempre é prejudicial e às vezes pode ser o mal menor.Se, por exemplo, você me der a opção de usar:
A) Uma biblioteca de imagens estável (imutável) e minúscula, bem testada , que duplica algumas dúzias de linhas de código matemático trivial para matemática vetorial, como produtos com pontos, lerps e grampos, mas é completamente dissociada de qualquer outra coisa e se desenvolve em uma fração de um segundo.
B) Uma biblioteca de imagens instável (que muda rapidamente) que depende de uma biblioteca matemática épica para evitar as dúzias de linhas de código mencionadas acima, com a biblioteca matemática instável e constantemente recebendo novas atualizações e alterações, e, portanto, a biblioteca de imagens também precisa ser reconstruído se não completamente alterado também. Demora 15 minutos para limpar tudo.
... então, obviamente, deve ser um acéfalo para a maioria das pessoas que A, e realmente precisamente devido à sua pequena duplicação de código, é preferível. A ênfase principal que preciso enfatizar é a parte bem testada . Obviamente, não há nada pior do que ter um código duplicado que nem funciona em primeiro lugar; nesse momento, ele está duplicando bugs.
Mas há também o acoplamento e a estabilidade em que pensar, e alguma duplicação modesta aqui e ali pode servir como um mecanismo de dissociação que também aumenta a estabilidade (natureza imutável) do pacote.
Portanto, minha sugestão será focar mais nos testes e tentar criar algo realmente estável (como imutável, encontrando poucas razões para mudar no futuro) e confiável cujas dependências de fontes externas, se houver alguma, são muito estável, tentando eliminar todas as formas de duplicação na sua base de código. Em um ambiente de equipe grande, o último tende a ser um objetivo impraticável, sem mencionar que ele pode aumentar o acoplamento e a quantidade de código instável que você possui em sua base de código.
fonte
Não esqueça que a duplicação de código nem sempre é prejudicial. Imagine: agora você tem alguma tarefa a ser resolvida em módulos absolutamente diferentes do seu projeto. Agora mesmo é a mesma tarefa.
Pode haver três razões para isso:
Algum tema em torno desta tarefa é o mesmo para os dois módulos. Nesse caso, a duplicação de código é ruim e deve ser liquidada. Seria inteligente criar uma classe ou um módulo para apoiar esse tema e usar seus métodos nos dois módulos.
A tarefa é teórica em termos de seu projeto. Por exemplo, é de física ou matemática, etc. A tarefa existe independentemente no seu projeto. Nesse caso, a duplicação de código é ruim e também deve ser liquidada. Eu criaria uma classe especial para essas funções. E use essa função em qualquer módulo em que você precisar.
Mas em outros casos, a coincidência de tarefas é uma coincidência temporária e nada mais. Seria perigoso acreditar que essas tarefas permanecerão as mesmas durante as alterações do projeto devido à refatoração e até depuração. Nesse caso, seria melhor criar duas mesmas funções / partes de código em lugares diferentes. E as mudanças futuras em um deles não tocarão no outro.
E esse terceiro caso acontece com muita frequência. Se você duplicar "sem saber", é principalmente por esse motivo - não é uma duplicação real!
Portanto, tente mantê-lo limpo quando for realmente necessário e não tenha medo de duplicação, se não for necessário.
fonte
code duplication is not always harmful
é um péssimo conselho.