2009-10-22 4 views
13

Was sind die guten Möglichkeiten, mit komplizierter Geschäftslogik umzugehen, die auf den ersten Blick viele verschachtelte if-Anweisungen erfordert?Wie vereinfacht man komplizierte Business "IF" Logik?

Beispiel:

Rabatt-Coupon. sein könnte:

1a) Wert Rabatt
1b) Prozentualer Rabatt

2a) Normal Rabatt
2b) Progressive Rabatt

3a) Zugang Coupon
3b) Benötigen Sie benötigen keinen Zugriff Coupon

4a) Wird nur auf den Kunden angewendet, der bereits zuvor
gekauft hat 4b) Angelegt auf einen beliebigen Kunden

5a Kunde nur aus Ländern (X, Y Angewandte), ...)

Dieser Code noch komplizierter erfordert dann das:

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

Auch wenn Sie IFs ersetzen zu wechseln/Fall ist es immer noch zu kompliziert. Was sind die Wege, um es lesbar, wartbar, testbarer und leicht zu verstehen?

Antwort

4

Ich würde eine generische State-Machine schreiben, die auf Listen von Dingen zum Vergleich füttert.

9

Specification pattern könnte das sein, was Sie suchen.

Zusammenfassung:

In Computer-Programmierung, die Spezifikation Muster sind ein spezielles Software-Entwurfsmuster, wobei Business-Logik kann durch die Verkettung der Business-Logik zusammen boolean Logik rekombiniert werden.

2

Die objektorientierte Art und Weise, es zu tun ist, eine gemeinsame Schnittstelle mehr Rabatt-Klassen haben die Umsetzung:

dicsount.apply(order) 

die Logik Setzen Sie zur Bestimmung, ob die Reihenfolge innerhalb der Rabatt Klassen für den Rabatt qualifiziert.

+0

Es ist nicht verschiedene Rabattklassen. Es ist eine Rabattklasse mit all diesen 1-5 Dingen als mögliche Eigenschaften. – Zelid

+0

Ich schlage vor, dass Sie das ändern, so dass Sie mehrere Rabattklassen haben. –

+0

Ok, wie soll man dann angeben, dass der Rabatt gleich 1a, 2b, 3a, 4b sein soll? – Zelid

0

Mein erster Gedanke ist, dass dies nicht testbar ist, was mich zu einer Lösung führt, um es testbar zu bekommen.

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

Dann können Sie jede verschachtelte if-Anweisung als separaten Aufruf haben. Auf diese Weise können Sie sie einzeln testen und wenn Sie die große Gruppe testen, wissen Sie, dass jeder einzelne arbeitet.

+0

Dies ist wie es jetzt implementiert ist, noch CallFunctionX() benötigt viele IFs drin und so weiter ... – Zelid

+0

Vielleicht möchten Sie Ihre Logik bewerten. Wenn Sie beispielsweise die Funktionalität in Unterklassen gruppieren können, können Sie sehen, ob dieser Rabatt für eine Unterklasse gilt und sie dort weitergeben. –

0

Machen Sie Methoden, die nach einem bestimmten Fall suchen.

Bool IsValueNormalAndRequiresCoopon (Discount Rabatt) {...}

Bool IsValueNormalAndRequiresCoupon (Discount Rabatt) {...}

etc

Sobald Sie anfangen zu tun, dass es leichter zu sehen wird, wo man zwischen den Entscheidungen, gemeinsame Logik abstrakt. Sie können dann von dort aus gehen.

Für komplexe Entscheidungen habe ich oft eine Klasse, die die möglichen Zustände behandelt.

+1

machen ~ 32 solche Funktionen für alle Kombination von Eigenschaften ist nicht so gut zu – Zelid

+0

Bis Sie einige von ihnen fertig sind, sollten Sie anfangen, Möglichkeiten zu sehen, gemeinsame Prüfungen zu machen und den Code auf einfachere Struktur neu zu faktorisieren. Die Idee ist, einige der Bäume zu entfernen, so dass Sie beginnen können, den Wald zu sehen. – ElGringoGrande

1

Die Verwendung von guard clauses könnte etwas helfen.

+0

Aber verstößt dies nicht gegen das Prinzip des einzigen Ausgangs? –

+0

Definitiv. Obwohl ich nicht weiß, wie viel Bedeutung diese Regel hat. Ich gehe natürlich nicht so weit, um dem zu folgen. Selbst wenn Sie es abonnieren, halte ich Guard-Klauseln für eine akzeptable Ausnahme. –

+1

Guard-Klauseln erfordern eine neue Funktion für jede mögliche Kombination von 5 verschiedenen Rabatt-Coupon-Eigenschaften (Volumen/Prozentsatz, Normal/Progressiv, RequireCoupon/DoNotRequireCoupon, ...), die auch nicht gut ist – Zelid

11

Gute Frage. "Bedingte Komplexität" ist ein Code-Geruch. Polymorphism ist dein Freund.

Bedingte Logik ist unschuldig in den Kinderschuhen, wenn es einfach zu verstehen ist und innerhalb einer paar Zeilen Code enthalten ist. Leider altert es selten gut. Sie implementieren mehrere neue Funktionen und plötzlich Ihre bedingte Logik wird kompliziert und expansiv. [Joshua Kerevsky: Refactoring zu Patterns]

Eine der einfachsten Dinge, die Sie verschachtelte vermeiden tun können, wenn Blöcke zu lernen, ist Guard Clauses zu verwenden.

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

Die andere Sache, die ich Dinge gefunden haben ziemlich gut vereinfacht, und die Code selbsterklärend macht, ist Consolidating conditionals.

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

Weitere wertvollen refactoring Techniken im Zusammenhang mit bedingten Ausdrücken umfassen Decompose Conditional, Replace Conditional with Visitor und Reverse Conditional.

+0

+1 - Ich mag die Art, wie Sie gaben verschiedene Optionen. –

+1

Sieht aus wie "Guard Clauses" -Lösung funktioniert nur, wenn das Vergleichen von Attributen selbstausschließend ist (_isDead, _isSeparated, isRetired) ist es nicht möglich, diese Lösung für das mitgelieferte Beispiel zu verwenden, da der Rabatt-Coupon sowohl "1a) Wertrabatt, 2a) sein könnte Normaler Rabatt, 3b) Kein Zugangsschein erforderlich, 4a) Nur für Kunden, die bereits vorher gekauft haben und 5a) Nur für Kunden aus Ländern (X, Y, ...) " – Zelid

+0

oder in der Nähe von 32 solcher Rücksendungen: _isValueAndNoramAndAcees() _isValueAndProgressiveAndNotRequireAccessAndRequireCountires() .... alle anderen Kombinationen in IF und und in Funktion – Zelid

0

FWIW, ich habe Hamcrest sehr erfolgreich für diese Art von Sache verwendet. Ich glaube, man könnte sagen, dass es das Spezifikationsmuster implementiert, über das Arnis gesprochen hat.

1

Sie sollten wirklich

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

Google Tech Talks 20. November 2008

ABSTRACT

Ist Ihr Code voller if-Anweisungen sehen? Anweisungen wechseln? Haben Sie an verschiedenen Stellen die gleiche Switch-Anweisung? Wenn Sie Änderungen vornehmen, stellen Sie fest, dass Sie die gleiche Änderung an mehreren Stellen vornehmen, wenn/wechseln. Hast du jemals einen vergessen?

In diesem Vortrag werden Ansätze zur Verwendung objektorientierter Techniken zur Entfernung vieler dieser Bedingungen besprochen. Das Ergebnis ist ein sauberer, strafferer, besser konzipierter Code, der einfacher zu testen, zu verstehen und zu warten ist.

+0

Danke, wird lesen klingt sehr hilfreich – Zelid