Pergunta

Quais são as boas maneiras de lidar com a lógica do negócio complicado que a partir da primeira vista requer muitos aninhada if?

Exemplo:

Cupom de Desconto. poderia ser:

1a) Valor de desconto
1b) Percentagem de desconto

2a) Normal desconto
2b) Desconto progressivo

3a) Exige acesso coupon
3b) Não requer coupon acesso

4a) aplicada apenas para o cliente que já comprou antes
4b) aplicado a qualquer cliente

5a) Aplicada à cliente apenas de países (X, Y, ...)

Isso requer código ainda mais complicado, então esta:

if (discount.isPercentage) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isValue) {
    if (discount.isNormal) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    } else if (discount.isProgressive) {
        if (discount.requiresAccessCoupon) {
        } else {
        }
    }
} else if (discount.isXXX) {
    if (discount.isNormal) {
    } else if (discount.isProgressive) {
    }
}

Mesmo se você substituir IFs para switch / case ainda é muito complicado. Quais são as maneiras de tornar mais legível, de fácil manutenção, mais testável e fácil de entender?

Foi útil?

Solução

Eu ia escrever um genérico state-máquina que se alimenta de listas de coisas para comparar.

Outras dicas

Boa pergunta. "Condicional Complexidade" é um cheiro de código. Polimorfismo é seu amigo.

lógica condicional é inocente em sua infância, quando é simples de entender e contido dentro de um algumas linhas de código. Infelizmente, isso raramente idades bem. Você implementar vários novos recursos e de repente a sua lógica condicional torna-se complicado e expansivo. [Joshua Kerevsky: refatoração para Padrões]

Uma das coisas mais simples que você pode fazer para evitar aninhados se os blocos é aprender a utilizar cláusulas Guarda .

double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};  

A outra coisa que eu encontrei simplifica as coisas muito bem, e que torna o seu código de auto-documentação, é Consolidar condicionais .

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

Outras técnicas valiosas refatoração associados com expressões condicionais incluem Decompor condicional , Substituir condicional com Visitor, e condicional inversa .

Especificação padrão pode ser o que você está procurando.

Sumário:

Na programação de computadores, o padrão de especificação é um padrão de projeto de software em particular, em que a lógica do negócio pode ser recombinados encadeando a lógica de negócios em conjunto usando a lógica booleana.

A maneira orientada a objeto de fazê-lo é ter múltiplas classes de desconto execução de uma interface comum:

dicsount.apply(order)

Coloque a lógica para determinar se os qualifica para que o desconto dentro das classes de desconto.

Usando cláusulas guarda pode ajudar alguns.

FWIW, eu usei Hamcrest muito sucesso para este tipo de coisa. Eu acredito que você poderia dizer que ele implementa o padrão de especificação, @Arnis falado.

Você realmente deve ver

Clean Code Talks - Inheritance , Polimorfismo, & Testing
por Miško Hevery

O Google palestras técnicas 20 de novembro de 2008

RESUMO

é o código cheio de if? instruções switch? Você tem a mesma instrução switch em vários lugares? Quando você faz alterações que você encontrar-se fazer a mesma alteração para o mesmo se / switch em vários lugares? Alguma vez você esquecer um?

Esta palestra irá discutir abordagens para a utilização de técnicas Object Oriented para remover muitos desses condicionais. O resultado é mais limpo, mais apertado, código bem estruturados, que é mais fácil de teste, entender e manter.

Meu primeiro pensamento é que este não é testável, que me leva a uma solução, a fim de obtê-lo testável.

if (discount.isPercentage) {
  callFunctionOne(...);
} else if (discount.isValue) {
  callFunctionThree(...);
} else if (discount.isXXX) {
  callFunctionTwo(...);
}

Em seguida, você pode ter cada aninhados if ser uma chamada separada. Desta forma, você pode testá-los individualmente e quando você testar o grande grupo você sabe que cada indivíduo um funciona.

métodos fazem que os controlos para um caso particular.

bool IsValueNormalAndRequiresCoopon (desconto de desconto) {...}

bool IsValueNormalAndRequiresCoupon (desconto de desconto) {...}

etc

Uma vez que você começar a fazer isso, torna-se mais fácil ver onde você pode lógica comum abstrair entre as escolhas. Você pode então ir de lá.

Para decisões complexas que muitas vezes acabam com uma classe que lida com os estados possíveis.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top