Bryan Albrecht

Chain of responsibility Teil 2 Implementierung

Was Chain of responsibility genau ist und wie dies funktioniert, wurde bereits in Teil 1 erklärt. Im zweiten Teil soll nun die Implementierung erläutert werden.
Für jegliche Beispiele werde ich die Operationen eines einfachen Taschenrechner verwenden. Die wirklichen Anwendungsfälle sind jedoch meistens um ein vielfaches komplexer.

"Falsches" Anwendung

Das Programm eines Taschenrechners hat eine Methode, mit welcher eine Rechnung gestartet werden kann. Diese Methode brauch jeweils zwei Zahlen und einen Operator, damit eine Rechnung durchgeführt werden kann.
Da durchs mitgeben eines Operators dynamisch die richtig Methode aufgerufen werden soll, muss der Operator einer Methode zugeordnet werden können. Dies geht am einfachsten mit einem Switch Case:

  1. switch(type){
  2. case CalculationType.add:
  3. Calculator.Add() ;
  4. break ;
  5. case CalculationType.div:
  6. Calculator.Divide() ;
  7. break ;
  8. case CalculationType.mult:
  9. Calculator.Multiply() ;
  10. break ;
  11. case CalculationType.sub:
  12. Calculator.Substract() ;
  13. break ;
  14. }

Dieser Switch Case sieht noch einigermassen übersichtlich aus. Doch dies ändert sich schnell, wenn die Anzahl von verschiedenen Möglichkeiten zunimmt.

Das Klassendiagram nach UML Standart sieht wie folgt aus:

Klassendiagram Falsch

Verbesserte Anwendung

Um das Programm übersichtlicher und wartbarer machen zu können, wird nun das Chain of reponsiblity Pattern angewandt.

Interface

Damit alle «Kettenelemente» die benötigten Funktionen und Properties haben, wird ein Interface verwendet, welches wie folgt aufgebaut ist:

IChain Interface

Benannt wird das Interface IChain, weil es für die Kettenelemente verwendet wird.

Klassen für die Kette

Für jedes Kettenelement wird eine neue Klasse benötigt. Diese Klasse implementieren alle das IChain Interface.
In diesem Beispiel werden nur vier Klassen für die Kette verwendet. Dies ist jedoch unendlich erweiterbar.

Das Klassendiagram des Programms sieht nun wie folgt aus:

Klassendiagram Richtig

Jede einzelne diese Klassen, welche in der Kette verwendet werden können, hat einen ähnlichen Aufbau. Die Methode Calculate, welche vom IChain Interface implementiert wird, überprüft ob die Klasse für den angegebenen Calculation Type zuständig ist.

Wenn Ja, wird die Rechnung ausgeführt und ein Ergebnis ausgegeben.

Wenn jedoch die Klasse nicht für den angegebenen Typ zuständig ist, wird die Calculate Methode des folgenden Kettenelements aufgerufen.

Die Calculate Methode von der Multiply Methode sieht im Beispiel wie folgt aus:

  1. public void Calculate(CalculateObject calculateObject)
  2. {
  3. if(calculateObject.CalcType == CalculationType.mult)
  4. {
  5. Console.WriteLine(calculateObject.FirstNumber * calculateObject.SecondNumbe);
  6. }
  7. else
  8. {
  9. NextChain.Calculate(calculateObject);
  10. }
  11. }

Kette erstellen

Bevor die Kette verwendet werden kann, muss diese erst einmal erstellt werden. Dies wird am besten in einer extra «Helper Klasse» gemacht, da sonst bei grossen Ketten eine unnötige Menge an Code entsteht und die Wartbarkeit erneut nicht gewährleistet ist.

Beim Erstellen der Kette geht man am besten von hintersten bis zum ersten Kettenglied vor. Dies ist so, da dem Kettenelement jeweils das nachfolgende mitgegeben werden muss. Das hinterste Kettenelement besitzt jedoch kein folge Element.

  1. IChain chainelement4 = new Divide();
  2. IChain chainelement3 = new Multiply() { NextChain = chainelement4};
  3. IChain chainelement2 = new Subtract() { NextChain = chainelement3 };
  4. IChain chainelement1 = new Add() { NextChain = chainelement2 };

Kette verwenden

Das verwenden der Kette ist der simpelste Teil des Programms. Sobald das erste Element aufgerufen wurde, macht die Kette alles von alleine.

Das erste Element wird im Beispiel wie folgt aufgerufen:

  1. chainelement1.Calculate(); //Parameter müssen noch hinzugefügt werden.

Guthub

Ein vollständiges Beispielprojekt ist auf Github zu finden.