Entwurfsmuster in Java Das Observer- und das Decorator-Pattern Friederike Löwe 13. März 2011
Inhaltsverzeichnis Einleitung Wozu eigentlich Entwurfsmuster? Die Grundlage: Design-Prinzipien Das Decorator-Pattern Das Muster am Beispiel erklärt Dekorierer aus der Praxis: Java I/O Das Observer-Pattern Allgemein: Was ist das Observer-Pattern? Observer in Java Ausblick Wie geht s weiter? Andere Entwurfsmuster Referezen
Einleitung
Wo ist der Unterschied?
Irgendjemand hat deine Probleme schon gelöst. Die meisten Probleme, die einem beim Programmieren begegnen können, hat schon einmal jemand gelöst. Besonders erfolgreiche Lösungsstrategien, die sich für eine bestimmte Art von Problem bewährt haben, wurden als Lösungsmuster gesammelt.
Grundregeln
Entwurfsprinzip Programmiere auf eine Schnittstelle, nicht auf eine Implementierung.
Entwurfsprinzip Identifiziere die Aspekte deiner Anwendung, die sich ändern können und trenne sie von denen, die konstant bleiben.
Entwurfsprinzip Ziehe Komposition der Vererbung vor.
Entwurfsprinzip Strebe bei deinen Entwürfen mit interagierenden Objekten nach lockerer Kopplung.
Entwurfsprinzip Klassen sollten für Erweiterung offen, aber für Veränderung geschlossen sein.
Was sind Entwurfsmuster?
Das Decorator-Pattern
Ein Kaffeehaus... ursprüngliches Bestellsystem
Extras Zum Kaffee kann man verschiedene Zutaten dazu bestellen, wie etwa: heiße Milch Sojamilch Schokolade Milchschaum... Jede dieser Zutaten kostet natürlich extra. Wie kann man das in das Bestellsystem integrieren?
Ein erster Versuch...
Neuer Ansatz!
Problempotential? Preisänderungen bei den Zutaten Bearbeitung von bestehendem Code neue Zutaten neue Methoden in der Superklasse, Ändern der preis()-methode Einführen neuer Getränke Eistee mit Milchschaum?? Was ist, wenn ein Kunde Doppelschoko wünscht? Was noch...?
So geht es: Das Decorator-Muster Ein Getränk wird mit Zutaten dekoriert, z.b bestellt ein Kunde eine dunkle Röstung mit Schoko und Milchschaum. 1. Wir nehmen ein DunkleRöstung-Objekt, 2. dekorieren es mit einem Schoko-Objekt, 3. dekorieren es mit einem Milchschaum-Objekt, 4. rufen die Methode preis() auf und stützen uns auf Delegierung um den Preis für die Zutaten hinzuzufügen.
Ein Getränk mit Dekorieren aufbauen Wir nehmen uns ein DunkleRöstung-Objekt,...
Ein Getränk mit Dekorieren aufbauen...erzeugen uns ein Schoko-Objekt und packen es um DunkleRöstung,...
Ein Getränk mit Dekorieren aufbauen...erstellen einen Milchschaum-Dekorierer und packen Schoko damit ein.
Ein Getränk mit Dekorieren aufbauen: Zwischenstand Dekorierer haben den gleichen Supertyp wie die Objekte, die sie dekorieren. Man kann ein oder mehr Objekte verwenden, um ein Objekt einzupacken. Da der Dekorierer den gleichen Supertyp wie das dekorierte Objekt hat, können wir das dekorierte Objekt an der Stelle des ursprünglichen (jetzt eingepackten) Objekts herumreichen. Der Dekorierer fügt sein eigenes Verhalten hinzu, bevor und/oder nachdem der Aufruf an das dekorierte Objekt delegiert wurde, um die Arbeit abzuschließen. Objekte können jederzeit dekoriert werden. Wir können Objekte also zur Laufzeit dynamisch mit so vielen Dekorierern dekorieren, wie es uns gefällt.
Ein Getränk mit Dekorieren aufbauen
Dekorierer aus der Praxis: Java I/O BufferedInputStream und LineNumerInputStream erweitern beide FilterInputStream, die als abstrakte Dekorierer-Klasse dient.
Java I/O Klassendiagramm BufferedInputStream und LineNumerInputStream erweitern beide FilterInputStream, die als abstrakte Dekorierer-Klasse dient.
Das Observer-Pattern
Das Observer-Muster Das Observer-Muster ermöglicht einem oder mehreren Objekten, automatisch auf die Zustandsänderung eines bestimmten Objekts zu reagieren, um den eigenen Zustand anzupassen.
ein Zeitungsabonement
Das Observer-Muster: Schema Das Observer-Muster definiert eine Eins-zu-viele-Abhängigkeit zwischen Objekten in der Art, dass alle abhängigen Objekte benachrichtigt werden, wenn sich der Zustand des einen Objekts ändert.
Das Observer-Muster: Klassendiagramm
Observable und Observer in Java
Ein Beispiel
Ausblick
Irgendjemand hat also deine Probleme schon gelöst. Am besten nutzt man Entwurfsmuster, indem man sein Gedächtnis mit ihnen füttert und in bestehenden Anwendungen die Stellen erkennt, an denen man sie einsetzten kann. Statt Code-Wiederverwertung bieten Muster Erfahrungs-Wiederverwertung.
Wie finde ich ein passendes Entwurfsmuster? 1. Klassifizierung nach der Aufgabe: Erzeugungsmuster betreffen den Prozess der Objekterzeugung Strukturmuster befassen sich mit der Zusammensetzung von Klassen und Objekten Verhaltensmuster charakterisieren die Art und Weise, in der Klassen und Objekte zusammenarbeiten und Zuständigkeiten aufteilen 2. Klassifizierung nach Gültigkeitsbereich: klassenbasiert (Beziehungen mit Hilfe von Vererbung, statisches Verhalten) objektbasiert (Beziehungen zwischen zusammenarbeitenden Objekten, dynamisch änderbares Verhalten)
Überblick über Entwurfsmuster C O Erzeugermuster Strukturmuster Verhaltensmuster Fabrikmethode Adapter (klassenbasiert) Interpreter Schablonenmethode Abstrakte Fabrik Adapter (objektbasiert) Befehl Erbauer Brücke Beobachter Prototyp Dekorierer Besucher Singleton Fassade Iterator Fliegengewicht Memento Kompositum Strategie Proxy Vermittler Zustand Zuständigkeitskette
Erzeugungsmuster: Fabrikmethode Definiere eine Klassenschnittstelle mit Operationen zum Erzeugen eines Objekts, aber lasse Unterklassen entscheiden, von welcher Klasse das zu erzeugende Objekt ist. Fabrikmethoden ermöglichen es einer Klasse, die Erzeugung von Objekten an eine Unterklasse zu delegieren.
Erzeugungsmuster: Singleton Sichere ab, dass eine Klasse genau ein Exemplar besitzt, und stelle einen globalen Zugriffspunkt darauf bereit.
Strukturmuster: Adapter Passe die Schnittstelle einer Klasse an eine andere von ihren Klienten erwartete Schnittstelle an. Das Adaptermuster lässt Klassen zusammenarbeiten, die wegen inkompatibler Schnittstellen ansonsten dazu nicht in der Lage wären.
Strukturmuster: Dekorierer Erweitere ein Objekt dynamisch um Zuständigkeiten. Dekorierer bieten eine flexible Alternative zur Unterklassenbildung, um die Funktionalität einer Klasse zu erweitern.
Strukturmuster: Fassade Biete eine einheitliche Schnittstelle zu einer Menge von Schnittstellen eines Subsystems. Die Fassadenklasse definiert eine abstrakte Schnittstelle, welche die Verwendung des Subsystems vereinfacht.
Verhaltensmuster: Beobachter Definiere eine 1-zu-n-Abhängigkeit zwischen Objekten, so dass die Änderung des Zustands eines Objekts dazu führt, dass alle abhängigen Objekte benachrichtigt und automatisch aktualisiert werden.
Verhaltensmuster: Iterator Biete eine Möglichkeit, um auf die Elemente eines zusammengesetzten Objekts sequentiell zugreifen zu können, ohne die zugrundeliegende Repräsentation offenzulegen.
Verhaltensmuster: Strategie Definiere eine Familie von Algorithmen, kapsele jeden einzelnen und mache sie austauschbar. Das Strategiemuster ermöglicht es, den Algorithmus unabhängig von ihn nutzenden Klienten zu variieren.
Eine Warnung zum Schluss... Software-Entwürfe sollten so einfach wie möglich sein. Wenn man ein Pattern ausgewählt hat, bleibt die Frage: Bietet mir der Entwurf mit dem Pattern erhebliche Vorteile bezüglich Flexibilität Performance oder Wiederverwertbarkeit? In Zweifelsfällen wählen Sie den einfacheren Weg. Karl Eilebrecht / Gernot Starke
Referezen
Quellen & Literatur I Freeman, Elithabeth und Eric: Head First - Design Patterns O Reilly, Cambridge, 2004 Eilebrecht, Karl und Starke, Gernot: Patterns kompakt: Entwurfsmuster für effektive Software-Entwicklung Spektrum, Heidelberg, 2010 http://dx.doi.org/10.1007/978-3-8274-2526-3 Gamma, Erich et.al: Entwurfsmuster Addison-Wesley, Bonn, 1996
Quellen & Literatur II Ullenboom, Christian: Java ist auch eine Insel Galileo Press, Bonn, 2005