2009-10-22 10 views
13

Quali sono i modi migliori per gestire una complessa logica aziendale che, a prima vista, richiede numerose istruzioni nidificate if?Come semplificare la logica "IF" del business complicato?

Esempio:

Buono sconto. potrebbe essere:

sconto 1a) Valore
1b) Percentuale sconto

2a) sconto normale
2b) riduzione progressiva

3a) Richiede accesso coupon
3b) non richiedono accesso coupon

4a) Applicato solo al cliente che ha già acquistato prima
4b) Applicato a qualsiasi cliente

5a) Applicato al cliente solo da paesi (X, Y, ...)

che richiede il codice ancora più complicato allora questo:

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) { 
    } 
} 

Anche se si sostituisce FI per passare/caso è ancora troppo complicato Quali sono i modi per renderlo leggibile, mantenibile, più testabile e facile da capire?

risposta

4

Scriverei una macchina di stato generica che si alimenta in elenchi di cose da confrontare.

9

Specification pattern potrebbe essere quello che stai cercando.

Sommario:

Nella programmazione di computer, il modello di specifica è un particolare design pattern, per cui la logica di business può essere ricombinato per concatenazione logica di business insieme usando la logica booleana.

2

Scopo orientato modo di farlo è di avere più classi sconti attuazione un'interfaccia comune:

dicsount.apply(order) 

Mettere la logica per determinare se l'ordine qualifica per lo sconto all'interno delle classi di sconto.

+0

Non si tratta di classi di sconto diverse. È una classe di sconto con tutte le cose 1-5 come proprietà possibili. – Zelid

+0

Ti suggerisco di cambiarlo in modo da avere più classi di sconto. –

+0

Ok, come quindi specificare che lo sconto dovrebbe essere 1a, 2b, 3a, 4b allo stesso tempo? – Zelid

0

Il mio primo pensiero è che questo non è testabile, il che mi porta a una soluzione, al fine di farlo testabile.

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

Quindi è possibile che ogni istruzione nidificata sia una chiamata separata. In questo modo puoi testarli individualmente e quando provi il gruppo numeroso sai che ognuno lavora.

+0

Questo è come se fosse implementato ora, ancora CallFunctionX() richiede molti IF al suo interno e così via ... – Zelid

+0

Si consiglia di valutare la logica. Ad esempio, se è possibile raggruppare le funzionalità in sottoclassi, è possibile verificare se questo sconto si applica a una sottoclasse e trasferirla lì. –

0

Creare metodi che verificano un caso particolare.

bool IsValueNormalAndRequiresCoopon (Sconto) {...}

bool IsValueNormalAndRequiresCoupon (Sconto) {...}

ecc

Una volta che si inizia a fare che diventa più facile da vedere dove si può astrarre dalla logica comune tra le scelte. Puoi quindi andare da lì.

Per decisioni complesse, spesso finisco con una classe che gestisce gli stati possibili.

+1

rendere ~ 32 tali funzioni per tutte le combinazioni di proprietà non è così buono – Zelid

+0

Nel momento in cui si ottengono alcuni di essi, si dovrebbe iniziare a vedere le possibilità di effettuare controlli comuni e ridistribuire il codice a una struttura più semplice. L'idea è di rimuovere alcuni degli alberi in modo da poter iniziare a visualizzare la foresta. – ElGringoGrande

1

Utilizzare guard clauses potrebbe aiutare alcuni.

+0

Ma questo non viola il principio della singola uscita? –

+0

Definitivamente. Anche se non so quanto significhi questa regola ha più. Certamente non esco dal mio modo di seguirlo. Anche se lo sottoscrivi, penso che le clausole di salvaguardia sarebbero un'eccezione accettabile. Le clausole di protezione –

+1

richiederanno una nuova funzione per ogni combinazione possibile di 5 diverse proprietà del Buono Sconto (Volume/Percentuale, Normale/Progressiva, RequireCoupon/DoNotRequireCoupon, ...) che non va bene – Zelid

11

Buona domanda. "Complessità condizionale" è un odore di codice. Polymorphism è tuo amico.

logica condizionale è innocente nella sua infanzia, quando è semplice da capire e contenuta entro poche linee di codice. Sfortunatamente, invecchia raramente bene. Implementa diverse nuove funzionalità e improvvisamente la logica condizionale diventa complicata ed espansiva. [Joshua Kerevsky: Rifattorizzazione Patterns]

Una delle cose più semplici si può fare per evitare nidificato se i blocchi è quello di imparare ad usare Guard Clauses.

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

L'altra cosa che ho trovato semplifica le cose abbastanza bene, e che rende il vostro codice di auto-documentazione, è Consolidating conditionals.

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

Altri preziosi refactoring tecniche associate a espressioni condizionali sono Decompose Conditional, Replace Conditional with Visitor, e Reverse Conditional.

+0

+1 - Mi piace il modo in cui hai dato diverse opzioni. –

+1

Sembra che la soluzione "Clausole di protezione" funzioni solo se gli attributi di confronto sono autoesclusi (_isDead, _isSeparated, isRetired) non è possibile utilizzare questa soluzione per l'esempio fornito, perché Coupon di sconto può essere sia "1a) Valore di sconto, 2a) Sconto normale, 3b) Non richiedere coupon di accesso, 4a) Applicato solo al cliente che ha già acquistato prima e 5a) Applicato al cliente solo da paesi (X, Y, ...) " – Zelid

+0

o aggiungere circa 32 dichiarazioni di reso simili: _isValueAndNoramAndAcees() _isValueAndProgressiveAndNotRequireAccessAndRequireCountires() .... tutte le altre combinazioni in IF ee nella funzione – Zelid

0

FWIW, ho usato con successo Hamcrest per questo genere di cose. Credo che potresti dire che implementa il modello delle specifiche, di cui @Arnis ha parlato.

1

Si dovrebbe davvero vedere

Clean Code Talks - Inheritance, Polymorphism, & Testing
di Miško Hevery

Google Tech Talks 20 novembre 2008

RIASSUNTO

è il codice completo di istruzioni if? Cambiare le dichiarazioni? Hai la stessa dichiarazione di commutazione in vari punti? Quando apporti dei cambiamenti, ti ritrovi a fare lo stesso cambiamento allo stesso se/cambi in più punti? Hai mai dimenticato uno?

Questo intervento discuterà gli approcci all'utilizzo di tecniche orientate agli oggetti per rimuovere molti di questi condizionali. Il risultato è un codice più pulito, più stretto e meglio progettato che è più facile da testare, comprendere e mantenere.

+0

Grazie, leggerà i suoni molto utili – Zelid

Problemi correlati