Archiv der Kategorie: pattern

ScrumRechner Teil 6: Die Prozess/Ablauf-Sicht

Der Ablauf einer User-Interaktion zwischen den Schichten ist auf sehr abstrakter Ebene in folgendem Sequenzdiagramm dargestellt:

Ablauf-Sicht

Ablauf-Sicht

Schon auf dieser abstrakten Ebene stellt sich eine grundsätzliche Designfrage: Wer ist verantwortlich, aus den Tastendrücken eine Zahl zusammenzusetzen?

Das GRASP Controller Pattern definiert den Controller folgendermaßen:
Ein Controller ist das erste Objekt hinter der UI-Layer, das für die Verarbeitung der System-Events verantwortlich ist.
Zur Beantwortung der Frage, wo die Verantwortlichkeit zum Zusammensetzen der Zahl aus der Benutzereingabe stattfindet, ist eine Abwägung erforderlich: Ist das Zusammensetzen einer Zahl so kompliziert, dass es bereits einen (Sub-) Prozess des Systems darstellt? Wenn das der Fall ist, dann meldet die UI-Layer die einzelnen Ziffern an den Controller, und ermöglicht die Wiederverwendung dieser BusinessLogik für andere GUI-Technologien (in den UseCases gibt es einen Hinweis auf eine Portierung auf Mobiltelefone). Da es bei der Eingabe der StoryPoints nicht nur um natürliche Zahlen, sondern auch um Kommazahlen (die UseCases erwähnen halbe StoryPoints) geht, ist das Parsen der Zahlen hinreichend kompliziert, um einen Teil der BusinessLogik darzustellen und somit nicht in der View zu erledigen.

Scrum-Rechner Teil 5: Die logische Sicht als Teil des Konzepts

In diesem Beitrag wird die logische Sicht des Scrum-Rechners auf einer sehr abstrakten Ebene entwickelt. Die Anforderungen bestehen in den UseCases, von denen im Moment einer in Details untersucht wurde.
Die Frage, die mit der logischen Sicht beantwortet werden soll, ist: „Wie ist die Software konzeptuell organisiert?“. „Konzeptuell“ in diesem Zusammenhang bedeutet, dass nicht die Organisation in deploybaren Artefakten, sondern die logische Struktur untersucht wird. Detailfagen, die gestellt werden müssen, sind: „Welches sind die wichtigsten Schichten, Subsysteme, Packages, Frameworks, Interfaces und Klassen?“.

Traditionell ist eine Entkopplung der Darstellungsschicht von dem Rest des Systems gewünscht, um den „Impact of change“ bei neuen Anforderungen an die GUI (Designänderungen oder neue Technologien) gering zu halten. Während der traditionelle objektorientierte Weg besagt, dass jedes Objekt sich selbst darstellt (siehe GRASP: Information Expert), wird dieses Prinzip den nicht-funktionalen Anforderungen (hier: der Wartbarkeit) geopfert, und in View-Klassen ausgelagert (GRASP: Pure Fabrication). Microsoft bezeichnet die Trennung der View von dem Rest des Systems als „Document-View Architecture“.

Der UseCase beschreibt schon einige „Spielregeln“, nämlich die Abfolge Operand->Operator->Operand->…. Diese Regeln sollen von der Geschäftslogik (Durchführung der Berechnung) getrennt werden, sie werden also in einen Controller ausgelagert. Ergebnis wäre das Model-View-Controller-Pattern.

Wird für die bekannten Anwendungsfälle wirklich Model-View-Controller benötigt? Der UseCase „Daten eingeben“ erfordert zumindest keine Verbindung zwischen Modell und View. Im Zweifel gilt hier erst einmal: YAGNI. Statt MVC folgt der Entwurf in der logischen Sicht dem Model-View-Presenter Pattern.

logische Sicht des Scrum-Rechners

logische Sicht des Scrum-Rechners

Exceptions

Exceptions heißen übersetzt „Ausnahmen“. Das bedeutet, dass eine Exception durch eine nicht normale Situation hervorgerufen wird. Für die Definition einer nicht normalen Situation gibt es wiederum verschiedene Ansätze:

  • Der bequeme Ansatz geht nur von „happy path“ aus, alle Abweichungen davon sind Exceptions
  • Der extreme Ansatz kann auf alle Situationen entweder technisch oder fachlich reagieren und benötigt keine Exceptions

Der extreme Ansatz ist in der Praxis nicht möglich, erstens können nicht alle Fehler vorhergesehen werden, und zweitens können bei der Kompensation von Fehlern wiederum Fehler auftreten, somit ergibt sich ein endloses Programm. Der bequeme Ansatz kommt durchaus in der Praxis vor, er bürdet aber alle Verantwortung dem Aufrufer auf und ist somit unbeliebt. Zwischen diesen beiden Extremen liegt der pragmatische Ansatz, der eine praxisgerechte Mischung beider Konzepte ist.

Folgende Regeln helfen mir, einen gesunden Ansatz zu finden:

  • Eine Exception wird nie zur direkten Kontrolle des Programmflusses benutzt
  • Eine Exception bedeutet eine Situation, auf die nicht angemessen reagiert werden kann, das Werfen einer Exception verlagert dann die Reaktionsmöglichkeit in den Aufrufer
  • Aus Sicht des Clients kann eine Exception überall im try-Block eines try-catch-finally Statements auftreten, also kann der Kontrollfluss beim Auftreten der Exception irgendwo im try-Block stoppen und im catch-Block fortfahren. Der catch-Block stellt wieder einen konsistenten Zustand her
  • Jede Exception beinhaltet eine treffende Beschreibung der Ausnahmesituation
  • Exception-Handling wird nicht nachträglich eingefügt, sondern von Anfang an bei der Entwicklung berücksichtigt

Exceptions können nach unterschiedlichen Gesichtspunkten klassifiziert werden. Da für die Komponente, welche die Ausnahme wirft, alle Klassifizierungen „richtig“ sind, ist bei der Wahl der Klassifizierung auf die Bedürfnisse des Aufrufers Rücksicht zu nehmen. Für Klassifizierungsbeispiele siehe folgendes Bild.

Exceptions

Wird eine JEE-Applikation entwickelt, so erhalten die Exceptions eine zusätzliche Semantik bezüglich der Transaktionssteuerung: RuntimeExceptions, die eine ApplicationException-Annotierung besitzen und Checked-Exceptions bedeuten eine Fortsetzung der Transaktion. Fliegen hingegen RuntimeExceptions ohne spezielle Annotierung über EJB-Grenzen, so wird die Transaktion zurückgerollt. Bei der Entwicklung von EJBs ist diese Semantik immer zu berücksichtigen.

Generell gilt: Bei dem Design von Exceptions muss über Kompoententengrenzen hinweg gedacht werden, da die Bedürfnisse und Reaktionsmöglichkeiten der Aufrufers ebenfalls zu berücksichtigen sind.

GRASP Creator

Das GRASP Creator-Pattern gibt Antworten auf die Frage „Wer ist zuständig für die Erzeugung einer neuen Instanz einer bestimmten Klasse?“. Die Lösung soll folgende Eigenschaften besitzen:
– Lose Kopplung
– gute Verständlichkeit
– Kapselung
– Wiederverwendbarkeit

Das Creator-Pattern stellt einige Kriterien auf, die eine gute Lösung charakterisieren (auch hier darf man das Pattern nicht isoliert anwenden, sondern muß auch die anderen GRASP-Pattern berücksichtigen). Gegeben die Klassen A und B. A soll Instanzen von B erzeugen, wenn:
– A B enthält (Komposition oder Aggregation)
– A B verwaltet
– A B sehr eng benutzt
– A alle Informationen zur Erzeugung von B besitzt (Information Expert)
Je mehr dieser Kriterien zutreffen, desto deutlicher der Hinweis, das A Instanzen von B erzeugen soll.

Damit beantwortet das Creator-Pattern meiner Meinung nach auch folgende Frage (die zu Glaubenskriegen führen kann):
Wenn die Klasse A Instanzen von B erzeugt, und B eine obligatorische Kompositionsbeziehung zu C hat:

GRASP_creator
Eine Instanz von B muss immer eine Instanz von C enthalten, dies wird über den Konstruktor von B sichergestellt. Wenn A nun eine Instanz von B erzeugt, übergibt es dann als Konstruktor-Argument eine Instanz von C, oder aber alle einzelnen Informationen, die B zur Erzeugung von C benötigt?

Die Kriterien des Creator-Pattern liefern den Hinweis: Während die erste Lösung (A übergibt eine Instanz von C an den Konstuktor von B) lediglich den Punkt „besitzt alle Informationen, um C zu erzeugen“ erfüllt, deckt die andere Lösung alle Kriterien ab, und unterstützt somit die am Anfang dieses Texts genannten Eigenschaften der Lösung besser.

Lose Kopplung (auf Klassenebene)

Kopplung existiert auf mehreren Ebenen von Software. Systeme können miteinander stark verwoben sein, Komponenten können von einander abhängen, und Klassen können in verschiedenen Beziehungen zueinander stehen. Kopplung beschreibt, wie stark ein Element zu anderen Elementen verbunden ist, sie benutzt oder sie kennt. Die Stärke der Bindung zwischen Elementen bestimmt:

  • Wie einfach die Elemente wiederverwendet werden können
  • Ob sich die Elemente unabhängig voneinander verändern lassen („change impact“)
  • Wie verständlich ein Element ohne Kenntnis anderer Elemente ist

Kopplung zwischen Klassen existiert und muss auch existieren, sie kann höchstens reduziert werden, um die oben aufgezählten Nachteile zu verhindern. Dafür muss verstanden werden, welche Kopplungsarten „eng“ (oder „stark“) sind, und welche „lose“. Das folgende Bild zeigt verschiedene Arten von Kopplung. Dabei sinkt der Kopplungsgrad von oben nach unten, eine Vererbung zwischen zwei Klassen ist eine stärkere Kopplung als eine Dependency.Lose Kopplung

Auf der rechten Seite des Bildes tauchen Interfaces auf, die Kopplung zu einem Interface ist schwächer als zu einer Klasse (da die Implementierung abstrahiert wird). Trotzdem ist es schwierig, die Kopplung zu Interfaces im Vergleich zur Kopplung zu Klassen zu bewerten, denn neben der Stärke der Bindung ist auch die Stabilität der Elemente zu berücksichtigen: Eine enge Kopplung zu stabilen Elementen führt zu wenig Problemen, eine lose Kopplung zu variablen Elementen kann problematischer sein. Fast jede Klasse in Java besitzt eine enge Kopplung zu java.lang.String, trotzdem führt das nicht zu Problemen, weil String eine stabile und verbreitete Klasse ist.

Lose Kopplung ist ebenfalls ein Pattern (GRASP: Low Coupling), es unterstützt Designentscheidungen, stellt aber das Ziel der losen Kopplung nicht über andere Ziele.

GRASP: Information Expert

Bei einer Diskussion mit Kollegen kam die Frage auf, welche Komponente für das Zählen der ungelesenen Nachrichten eines EMail-Postfachs zuständig ist. Speziell ging es dabei um eine App im Browser, die über einen Backend-Service auf ein EMail-Postfach zugreift. Folgendes Bild stellt eine vereinfachte Übersicht der vorhandenen Klassen dar:

Information Expert
Die Fragestellung („Wer ist für etwas zuständig?“) gibt bereits den Hinweis, dass hier GRASP (General Responsibility Assignment Software Patterns) weiterhelfen kann. Patterns bestehen aus fünf Abschnitten:
* Ein Name
* Eine (generalisierte) Problemstellung
* Eine Lösung / ein Ratschlag
* Eine Diskussion der Lösung
* Gegenanzeigen zur Verwedung des Patterns

Zurück zu dem Problem: „Wer zählt die ungelesenen Nachrichten eines EMail-Postfachs?“.Das Pattern „Information Expert“ gibt auf die Problemstellung „Welches Objekt soll für Aufgabe XYZ zuständig sein?“ den Ratschlag: „Das Objekt, welches alle nötigen Informationen zur Erfüllung der Aufgabe hat“. In unserem Beispiel besitzen die beiden verschiedenen EMail-Klassen (im Package „app“ und „backend“) zwar die Information, ob sie bereits gelesen wurden, allerdings besitzen sie keine Referenz auf alle Mails eines Postfachs. Also sollte laut „Information Expert“ das Zählen der ungelesenen Nachrichten nicht von den beiden EMail-Klassen (bzw. deren Instanzen) vorgenommen werden.

Die Klasse „EMailService“ liefert zu einer bestimmten PostfachID die ersten 50 EMails und hat keine permanente Referenz auf Postfächer. Interessanterweise wollten die meisten Kollegen die Zählfunktionalität hier einbauen. Wenn man sich aber den Zählalgorithmus an diese Stelle denkt (hole das Postfach XYZ, iteriere durch alle Emails und zähle die mit read = false), so sieht man, dass der Service mit den EMail-Objeten sprechen müsste. Das ist gegen das „Don’t talk to strangers“-Prinzip und hat Nachteile, wenn die EMail-Klasse verändert wird (da dann sowohl der EMailService, als auch die Inbox angepasst werden müssen).

Übrig bleiben die Klassen „EMailApp“ und „Inbox“. Jetzt sind genauere Anforderungen notwendig, um mit dem Information Expert Pattern hier eine Enscheidung treffen zu können. Sollen alle ungelesenen Nachrichten des gesamten Postfachs gezählt werden? Dann besitzt die „Inbox“ alle Informationen. Oder sollen nur die ungelesenen Nachrichten des angezeigten EMail-Postfachs (also der ersten 50 Nachrichten) gezählt werden? Dann besitzt die „EMailApp“ alle Informationen.

Das Information Expert Pattern gibt stets nur Ratschläge, die man nie isoliert betrachten sollte, sondern immer auch die anderen Pattern von GRASP konsultieren sollte. Geht man von dem Domainmodel aus, so stellt sich irgendwann die Frage: „Wer ist dafür zuständig, das Postfach darzustellen?“. Laut Information Expert zeichnet sich das Postfach selbst auf dem Bildschirm, schreibt und liest sich aus der Datenbank und hat auch sonst alle Aufgaben, für die es alle Informationen besitzt. Genau dafür gibt es aber andere GRASPs, die dem Ratschlag des Information Expert Patterns widersprechen (z.B. in diese Fall das „Pure Fabrication“ Pattern). Es darf also nie ein Pattern für sich alleine betrachtet werden.

Sehr gute Informationen zu GRASP gibt es in dem Buch „Applying UML and Patterns“ von „Craig Larman“, welches ich auch auf der Website Modellierung.net verlinkt habe.