Que tipo de perguntas determinam se um código semelhante é separado ou comum?

4

Eu me deparei com o seguinte problema muitas vezes em várias formas na minha carreira de programação. Como um exemplo simples, considere o seguinte:

Digamos que existe um método (B) que pode diferir dependendo dos requisitos do cliente. Isso pode ser por vários motivos. O senso comum diz a você que, como as coisas estão agora, você deve escrevê-las como um método comum e adicionar código de ramificação no futuro para lidar com futuros casos de borda.

Customer 1: A-->B-->C

Customer 2: A-->B-->C

No entanto, sua experiência diz que esse código em particular provavelmente será alterado, por isso seria mais fácil ter métodos separados (B1 e B2) e alterar a lógica de um cliente, em vez de adicionar continuamente o método de criação de código de ramificação ( B) não genérico.

Customer 1: A-->B1-->C

Customer 2: A-->B2-->C

Existe alguma regra que pode ser aplicada em tais casos ou cada caso tem que ser levado em consideração pelos seus méritos?

Como um aparte, eu me deparei com muitas pessoas que defendem firmemente uma abordagem sobre a outra e eu posso ver os méritos de ambas. Mas certamente há algum meio-termo que pode ser alcançado com base em argumentos fundamentados?

    
por Robbie Dee 11.03.2013 / 12:51
fonte

5 respostas

4

Em geral, a lógica de negócios tende a ser semelhante por coincidência. Por outro lado, a lógica do programa (que significa a estrutura e o fluxo da sua aplicação) tende a ser similar por convenção.

Se você pensar em diferentes tipos de entidades de negócios, todos terão um método fileTaxReturn (é a época), mas há muito poucas razões para que eles usem o mesmo método, porque a forma como os arquivos LLC seus impostos são completamente diferentes da forma como a C Corp registra seus impostos. Esta é a lógica de negócios.

Por outro lado, se você tem algum tipo de comportamento (talvez seja um emissor de evento que fica viciado por registrar) que faz parte da lógica do programa, é muito provável que seja uma convenção do aplicativo e possa ser reutilizada.

Existem dois aspectos orientados à manutenção desta questão.

  1. Polimorfismo implícito - para a lógica de negócios, muitas vezes faz muito sentido para abstrair prematuramente, porque senão, à medida que você adiciona mais casos básicos, todos os seus métodos se tornarão feios alternar instruções. Ao fazer com que os casos de negócios definam cenários claros desde o início, isso torna a intenção visível e evita o inferno da instrução switch.

  2. Abstração prematura - Por outro lado, se você está dizendo para si mesmo "Talvez nós vamos querer logar no banco de dados, mas às vezes para um arquivo simples, e outras vezes para um feed RSS, e talvez Twitter , ou Facebook, ou uma porta serial ... ", você está fazendo abstração prematura - criando complexidade desnecessária em um sistema e uma confusão total para o programador de manutenção. O código bem encapsulado é fácil de refatorar e não sofre de falta de abstração.

por 11.03.2013 / 14:52
fonte
5

Sim, cada caso precisa ser levado a seu próprio mérito, já que não há uma regra geral que diga "todos os ramos devem estar em seus próprios métodos".

A ramificação precisa ser feita em ambos os casos. É só uma questão de decidir se você quer colocá-lo em B em seu primeiro exemplo, ou em A como em seu segundo. Decidir como fazer resume-se a duas coisas:

  1. Legibilidade: o código é mais legível se você colocar ramificações em A vs. B?
  2. Manutenção: O código é mais fácil de manter se você colocar ramificações em A vs. B?

Mais frequentemente do que não, estes dois andam de mãos dadas. Código que é fácil de entender tende a ser mais fácil de mudar. Comece com o caso mais simples possível (todo o código em um só lugar) e depois extraia o material até chegar a um ponto em que o código seja curto e doce, mas ainda assim compacto o suficiente para mostrar os detalhes.

Quando (se) você precisa adicionar B3 e B4, você tem um candidato para introduzir algo um pouco mais flexível, como criar uma superclasse comum e quais não. Mas não antes disso.

    
por 11.03.2013 / 13:17
fonte
5

Are there any rules of thumb that can be applied in such cases or does every case have to be taken on its merits?

Há pelo menos uma ou duas dúzias de tipos diferentes de soluções para esse tipo de problema, e escolher o "melhor" (o que quer que isso signifique) depende dos detalhes da situação específica. Isso não significa que não há "nenhuma regra prática" - há muito, mas eu acho que mais do que faria sentido tentar listá-los aqui em uma postagem.

Disse que, aqui estão algumas coisas que podem influenciar sua decisão:

  • o que "aciona" a ramificação? Você precisa de um tempo de compilação ou de uma opção de tempo de execução?
  • quão grandes são as diferenças entre B1 e B2? Alguém tem que duplicar o código em B1 e B2? Você vai duplicar código fazendo "A - > B1 - > C" e "A - > B2 - > C" (o que seria uma coisa ruim)?
  • B está "muito grande" ou "muito complexo" (e por que B não ramifica entre B1 e B2)?
  • você usa OOP e são partes A, B e C de uma classe em que a aplicação de "padrão de método de modelo" pode ser a coisa certa a fazer?
  • você tem uma linguagem de programação à mão onde, em vez de chamar B1 ou B2, você poderia injetar um retorno de chamada para B1 ou B2 no código que precisa chamar B1 ou B2?
  • a decisão "B1 vs. B2" deve ser tomada em apenas um lugar no código, ou há muitos lugares onde o resultado dessa decisão é avaliado (e a troca entre B1 e B2 deve ser feita novamente?)
por 11.03.2013 / 13:12
fonte
1

B é o comportamento. Portanto, tenha uma interface para isso e introduza novas implementações conforme necessário. Equipe instâncias de objetos diferentes com diferentes implementações ao instanciá-las. Desta forma você fica com o Open Closed Principle , porque você só precisa implementar uma interface e configurar seu uso. O proprietário dessa instância de interface não será afetado pela introdução de novas implementações de interface.

Desta forma, você também está separando a ramificação, que é um problema de configuração e a execução real. O código ficará muito mais legível, porque não haverá ramificações durante a execução da lógica de negócios.

    
por 11.03.2013 / 16:04
fonte
0

KISS & YAGNI

... simples & sistemas intuitivos são geralmente mais fáceis de evoluir / adaptar do que sistemas complexos que tentam cobrir todas as possíveis "evoluções em potencial" antecipadamente.

Se as diferenças forem menores, escolha um método / objeto / o que quer que seja.

Se as diferenças forem grandes, implemente-as em método / objeto / o que seja.

Meus 2 centavos

    
por 11.03.2013 / 17:06
fonte