GRASP
GRASP (General Responsibility Assignment Software Patterns) bezeichnet in der Softwaretechnik eine Menge von Entwurfsmustern, mit denen die Zuständigkeit bestimmter Klassen objektorientierter Systeme festgelegt wird.
Sie beschreiben also allgemein, welche Klassen und Objekte wofür zuständig sein sollten. Alle diese Regeln sind schon lange bekannt, sie wurden von Craig Larman einfach systematisch beschrieben. Dies erleichtert die Kommunikation zwischen Softwareentwicklern und erleichtert Einsteigern das Entwickeln eines Bewusstseins für guten bzw. schlechten Code.
Information Expert
Gibt dem Informations-Experten die Verantwortlichkeit, d. h. der Klasse, welche die notwendigen Informationen besitzt, um die Verantwortung wahrzunehmen. Beispiel: Eine Klasse Kreis, die die Methode berechneFläche() hat, die aus dem privaten Attribut Radius die Fläche berechnet. Negativ-Beispiel: Eine Klasse berechneFläche, die eine Methode hat, die eine geometrische Form entgegennimmt und deren Fläche berechnet. Im Unterschied zur realen Welt, in der ein Kreis gar nichts macht, muss in der objektorientierten Welt das Objekt alle Methoden besitzen, die Aktionen definieren, die mit ihm gemacht werden können.
Auch bekannt als „Do it Myself“ Strategie (Peter Coad). Information Expert ist das grundlegende Prinzip von Objektorientiertem Design und kann auch als Kapselung von Daten bezeichnet werden. Die konsequente Verwendung führt zu loser Kopplung (loose coupling) und hoher Kohäsion (high cohesion).
Creator
Das Erzeuger-Prinzip legt fest, wer eine Instanz einer Klasse (Objekt) erzeugen sollte. Neue Objekte der Klasse B sollten von A erzeugt werden, wenn:
- A eine Aggregation von B ist
- A B-Objekte enthält
- A B-Objekte erfasst
- A B-Objekte mit starker Kopplung verwendet
- A die Initialisierungsdaten für B hat (d. h. A ist Experte bezüglich Erzeugung von B)
Controller
Der Controller (Steuereinheit) beinhaltet das Domänenwissen und definiert, wer die für eine Nicht-Benutzeroberflächen-Klasse bestimmten Systemereignisse verarbeitet.
Es gibt hier zwei Möglichkeiten. Die Verwendung von Use-Case-Controllern oder Fassade-Controllern. Bei Use Case-Controllern werden alle Ereignisse eines Use Cases in einer Klasse behandelt. Mini Use Cases können auch in einem Controller behandelt werden, zum Beispiel das Erzeugen und Löschen eines Users. Wichtig ist nur, dass die Kohäsion des Controllers möglichst groß ist. Der Fassade-Controller wird in Message Handling Systemen verwendet, da hier alle Systemereignisse an einem Ort eintreffen. Hier wird ein einziger Controller (MessageHandler) definiert, der alle Ereignisse abfängt. Hierzu wird mit dem Command Pattern gearbeitet.
Low Coupling
Geringe Kopplung ist eines der Hauptziele von gutem Design. Kopplung bezeichnet hier das Maß für die Abhängigkeit eines Elementes (z. B. einer Klasse) von der Umgebung (z. B. von anderen Klassen). Die Hauptvorteile sind dabei die folgenden:
- leichte Anpassbarkeit, da Änderungen in einer Klasse keine Änderungen in anderen Klassen nach sich ziehen
- Verständlichkeit der Klasse, da der Kontext nicht betrachtet werden muss
- gute Testbarkeit
- hohe Wiederverwendbarkeit
Formen von Kopplung
Am Beispiel von der Klasse X zur Klasse Y:
- X ist direkte oder indirekte Unterklasse von Y
- X implementiert das Interface von Y
- X hat Attribut bzw. Referenz von Typ Y (Aggregation/Assoziation)
- X hat Methode, die Y referenziert (Abhängigkeit)
High Cohesion
Hohe Kohäsion ist vor allem wichtig, um die Komplexität von Gesamtsystemen zu begrenzen, indem man Klassen gut überschaubar organisiert. Die Kohäsion ist ein Maß für den inneren Zusammenhalt einer Klasse. Das heißt, sie misst, wie eng die Methoden und Attribute einer Klasse zusammenarbeiten.
Ein Negativ-Beispiel wäre z. B. eine Klasse, die Methoden aus zwei völlig verschiedenen Gebieten anbietet. Solche Klassen sind meistens schnell durch völlig nichtssagende Namen und viele Methoden/Codezeilen zu orten.
Zusammenhang High Cohesion / Low Coupling
Hohe Kohäsion innerhalb der Softwareeinheiten in einem System führt tendenziell zu geringer Kopplung zwischen den beteiligten Softwareeinheiten.
Polymorphismus
Polymorphismus kann verwendet werden, um das Verhalten abhängig vom Typ zu ändern. Somit können viele Fallunterscheidungen vermieden werden. Besser bekannt ist das Pattern als Strategy (GoF).
Pure Fabrication
Eine Pure Fabrication (reine Erfindung), stellt eine Klasse dar, die so nicht in der Problem Domain existiert. Sie stellt eine Methode zur Verfügung, für die sie nicht Experte ist. Normalerweise wird eine Pure Fabrication verwendet, um einen Algorithmus zu kapseln, der in keine Domain-Klasse passt. Sie kann zum Beispiel verwendet werden, um Technologiewissen von Domänenwissen zu trennen. Sie implementiert reines Verhalten und hat somit keinen Zustand. Sollte nicht zu häufig verwendet werden, sonst existieren am Schluss nur noch Klassen, die einzelne Methoden kapseln.
Indirection
Indirection (Umweg) kann verwendet werden, um geringe Kopplung zu erreichen. Sie wird erzielt, indem ein Vermittler zwischen Client und Server eingebaut wird. Sinnvoll wenn sich ein Serverobjekt ständig verändert. Als Nachteil ist die verminderte Leistungsfähigkeit zu berücksichtigen. Beispielhaft für dieses Muster ist die Einführung der Controller-Komponente, die zwischen dem Datenmodell (Model) und dessen Präsentation (View) im Model-View-Controller-Architekturmuster vermittelt.
Protected Variations
Interfaces sollen immer verschiedene konkrete Implementierungen verstecken. Man nutzt also Polymorphismus und Delegation, um zwischen den Implementierungen zu wechseln. Dadurch kann das restliche System vor den Auswirkungen eines Wechsels der Implementierung geschützt werden.
Literatur
- Mark Grand: Patterns in Java. 2. Auflage. John Wiley & Sons, 1999, ISBN 0-471-25841-5.
- Craig Larman: Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development. 3. Auflage. Markt und Technik, 2005, ISBN 3-8272-6898-2.