Entwurfsmuster
Entwurfsmuster
Entwurfsmuster
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
<strong>Entwurfsmuster</strong><br />
iTec<br />
1
Inhalt 11. Patterns und Frameworks<br />
q Einführung: was ist und wozu braucht man Patterns u. Frameworks ?<br />
q wie werden Patterns beschrieben ?<br />
q Beispiele für Designpatterns<br />
– Singleton<br />
– Proxy<br />
– State<br />
– Beobachter/Observer<br />
– Adapter<br />
– Kompositum<br />
– Dekorierer<br />
– Abstrakte Fabrik<br />
iTec<br />
2
Einführung 11. Patterns und Frameworks<br />
was ist und wozu braucht man Patterns und Frameworks?<br />
q Software-Entwicklung ist mühsam<br />
q wiederverwendbare Software zu schreiben, ist noch mühsamer<br />
q Hilfe bei dieser Mühe:<br />
Design-Patterns und Frameworks, die für häufig<br />
wiederkehrende Probleme bewährte Lösungsmuster<br />
anbieten<br />
iTec<br />
3
Einführung 11. Patterns und Frameworks<br />
Geschichte der Patterns<br />
q Christopher Alexander (1977): A Pattern Language<br />
wie können Standardlösungen immer wiederkehrender<br />
Innenarchitekturprobleme sprachlich formuliert werden?<br />
q Erich Gamma, R.Helm, R. Johnson, J. Vlissides (1995):<br />
Design Patterns – Elements of reusable OO-Software<br />
legten einen bis heute massgebenden Katalog von 23 Patterns vor<br />
q heute:<br />
es gibt kaum OO-Entwicklungen ohne Patterns !<br />
es gehört zum Grundvokabular eines jeden SW-Ingenieurs !<br />
iTec<br />
4
Einführung 11. Patterns und Frameworks<br />
zunächst einige wichtige Begriffe<br />
Software Design<br />
Die Aktivitäten, die aus den Anforderungen an ein<br />
Softwaresystem eine Softwarelösung entwerfen.<br />
Diese Lösung beschreibt die Systemstruktur als<br />
Systemarchitektur (siehe nächste Folie) und dient der<br />
Implementation des Systems als Blueprint.<br />
.<br />
q der Design enthält nicht die Implementation des Systems<br />
q der Design enthält die eigentliche Lösung eines Problems<br />
q der Design wird methodisch und mit Diagrammtechniken erarbeitet<br />
iTec<br />
5
Einführung 11. Patterns und Frameworks<br />
wichtige Begriffe<br />
Software-Architektur<br />
eine Beschreibung der Teilsysteme und Komponenten eines<br />
Softwaresystems und deren Beziehungen untereinander.<br />
Teilsysteme und Komponenten werden unter verschiedenen<br />
Blickwinkeln betrachtet, um verschiedene funktionale und<br />
nichtfunktionale Aspekte zu beschreiben.<br />
Eine Softwarearchitektur ist das Resultat des Software-Designs.<br />
q Komponenten sind gekapselte, mit Schnittstellen versehene<br />
Systemteile: Module, Bibliotheken, Klassen/Objekte<br />
q nichtfunktionale Aspekte: z.B. Erweiterbarkeit, Zuverlässigkeit,<br />
Kosten, Wartbarkeit, Portierbarkeit<br />
q Beziehungen: zB. statisch, dynamisch, abhängig, enthalten, abgeleitet<br />
iTec<br />
6
Einführung 11. Patterns und Frameworks<br />
wichtige Begriffe<br />
Framework<br />
Ein teilweise vorgefertigtes Softwaresystem das noch<br />
"instanziiert" werden muss durch Einfügen fehlender Teile.<br />
Dabei wird eine Architektur vordefiniert, in der wesentliche<br />
Komponenten vorgefertigt sind, und diejenigen Stellen werden<br />
genau definiert, in denen das Framework mit Implementationen<br />
für ein spezielles System noch instanziiert werden muss/kann.<br />
.<br />
q Frameworks sind die grösstmöglich vorgefertigten Systemrohbauten<br />
q Frameworks sind meist typisch für ganze Anwendungsbereiche, zB.:<br />
– für interaktive Systeme mit GUIs – MFC (C++), JFC (JAVA)<br />
– für verteilte OO-Systeme – CORBA, DCOM<br />
iTec<br />
7
Einführung 11. Patterns und Frameworks<br />
Pattern Einordnung<br />
q Idiom<br />
Programmiermuster – wie in einer bestimmten Programmiersprache<br />
kleine Programmierprobleme gelöst werden.<br />
Beispiel in C/C++: wie man ein Feld durchläuft<br />
q Spezifischer Design<br />
ein spezifischer, persönlicher Design für ein bestimmtes Problem.<br />
Kann schlau sein, aber versucht nicht, allgemein anwendbar zu sein<br />
q Standard Design<br />
ein Design für ein bestimmtes Problem, der Allgemeinheit erlangt<br />
hat durch Wiederverwendung. Z.B. eine unternehmensweite Lösung<br />
für das Y2000-Problem<br />
q Design Pattern<br />
eine Lösung für eine ganze Klasse von ähnlichen Problemen. Meistens<br />
erst sichtbar nach längerer Anwendung von Standard-Designs.<br />
iTec<br />
8
Einführung 11. Patterns und Frameworks<br />
wie wird man ein guter Schachspieler?<br />
q lerne Regeln<br />
Figurenamen und –zugmöglichkeiten, Schachbrettgeometrie etc.<br />
q lerne Prinzipien<br />
relativer Figurenwert, strategischer Wert des Brettzentrums,<br />
Angriffsvermögen etc.<br />
q schliesslich und vor allem: lerne von Spielen der Meister<br />
Spielzüge und deren Anwendungssituationen: Muster (Patterns)<br />
es gibt hunderte von solchen Spielzügen/Strategien<br />
iTec<br />
9
Einführung 11. Patterns und Frameworks<br />
wie wird man ein guter Software-Designer?<br />
q lerne Regeln<br />
Algorithmen, Datenstrukturen, Programmiersprachen<br />
q lerne Prinzipien<br />
modulares Programmieren, objektorientiertes Programmieren<br />
q schliesslich und vor allem: lerne von SW-Designs der "Meister"<br />
Muster (Patterns) von allgemeinen Lösungen immer<br />
wiederkehrender Designprobleme<br />
iTec<br />
10
Einführung 11. Patterns und Frameworks<br />
Pattern Beispiel: Singleton<br />
q Absicht<br />
stellt sicher, daß von einer Klasse höchstens ein Objekt erzeugt<br />
wird, und stellt einen globalen Zugriff auf das Objekt bereit<br />
q Motivation<br />
in manchen Anwendungen dürfen einige Klassen nur eine<br />
Objektinstanz besitzen – z.B. ein Printer-Spooler Objekt, ein Firma<br />
Objekt, ein Windows Manager Objekt, ein Roboter Objekt mit<br />
Konfigurationsdaten zur Robotersteuerung, ein DB-Broker.<br />
Objekt in globaler Variablen<br />
keine Garantie für Einmaligkeit<br />
besser:<br />
lasse die Klasse selber ihren Konstruktor überwachen<br />
iTec<br />
11
Einführung 11. Patterns und Frameworks<br />
Beispiel: Singleton<br />
q Idee<br />
– verberge alle Konstruktoren nach aussen: mache sie private<br />
– biete eine neue Methode "instance" nach aussen an, die eine<br />
Referenz auf (das einzige) Objekt zurückgibt<br />
– diese Methode muss eine Klassenoperation sein (static)!<br />
warum?<br />
iTec<br />
12
Einführung 11. Patterns und Frameworks<br />
Beispiel: Singleton<br />
class Singleton {<br />
private static Singleton s = new Singleton();<br />
}<br />
private Singleton() { . . }<br />
public static Singleton Instance() { return s; }<br />
. . . //Attribute und Methoden<br />
das das Klassenattribut Klassenattribut sshält<br />
hält<br />
eine eine Referenz Referenz auf auf das das einzige einzige<br />
Objekt Objekt der der Klasse Klasse Singleton Singleton<br />
mindestens mindestens einen einen Konstruktor Konstruktor<br />
definieren definieren als als private: private:<br />
kein kein (öffentlicher) (öffentlicher) Default- Default-<br />
Konstruktor Konstruktor mehr mehr verfügbar verfügbar<br />
einzige einzige öffentliche öffentliche Zugriffsmethodmethode<br />
auf auf ein ein Singleton- Singleton-<br />
Zugriffs-<br />
Objekt: Objekt: ist ist Klassenmethode<br />
Klassenmethode<br />
Singleton s = Singleton.Instance();<br />
Benutzung: Benutzung: ssreferenziert referenziert das das<br />
einzige einzige Singleton-Objekt<br />
Singleton-Objekt<br />
iTec<br />
13
Einführung 11. Patterns und Frameworks<br />
Beispiel: Singleton (JAVA)<br />
class Singleton {<br />
private static Singleton s =<br />
new Singleton(99);<br />
private int i;<br />
private Singleton(int x) { i = x; }<br />
public static Singleton Instance() {<br />
return s;<br />
}<br />
public int getValue() { return i; }<br />
public void setValue(int x) { i = x; }<br />
}<br />
public class SingletonPattern {<br />
public static void main(String[] args) {<br />
Singleton s = Singleton.Instance();<br />
System.out.println(s.getValue());<br />
Singleton s2 = Singleton.Instance();<br />
s2.setValue(9);<br />
System.out.println(s.getValue());<br />
}<br />
}<br />
iTec<br />
Ausgabe: 99<br />
9<br />
14
Einführung 11. Patterns und Frameworks<br />
es gibt drei Pattern Typen<br />
1. Erzeugungsmuster (creational pattern)<br />
beschreiben Strukturen, die den Prozeß der Objekterzeugung<br />
enthalten. Das Anwendungsprogramm wird von der konkreten<br />
Realisation der Objekterzeugung entkoppelt. Es arbeitet auf<br />
einer höheren Abstraktionsebene und delegiert die Erzeugung<br />
der Objekte an die Erzeugungsstrukturen<br />
2. Strukturmuster (structural pattern)<br />
zeigen auf, auf welche Art und Weise Klassen bzw. Objekte zu<br />
größeren Strukturen zusammengefaßt werden können. Nahezu<br />
alle der vonStrukturmustern beschriebenen Strukturen<br />
entstehen zur Laufzeit, beruhen also auf der Technik der<br />
dynamischen Objektkomposition<br />
iTec<br />
15
Einführung 11. Patterns und Frameworks<br />
es gibt drei Pattern Typen<br />
3. Verhaltensmuster (behavioral patterns)<br />
beschreiben Strukturen, die am Kontrollfluß innerhalb der<br />
Anwendung beteiligt sind. Sie konzentrieren sich also auf<br />
Algorithmen und die Delegation von Zuständigkeiten.<br />
iTec<br />
16
Einführung 11. Patterns und Frameworks<br />
iTec<br />
17
Einführung 11. Patterns und Frameworks<br />
Vorteile von <strong>Entwurfsmuster</strong>n<br />
q Entwickler lernen schnell besseres Design<br />
q Entwickler werden produktiver<br />
q Qualität der Software wird besser<br />
q Kommunikation zwischen Entwicklern wird besser<br />
q Kommunikation bei der Wartung wird besser<br />
iTec<br />
18
Patternbeschreibung 11. Patterns und Frameworks<br />
q Name<br />
den wir als Kürzel für das Muster benutzen können, wenn wir uns darüber<br />
unterhalten.<br />
q Absicht kurze (1 – 2 Sätze) Beschreibung des Ziels des Patterns<br />
q Problem<br />
beschreibt die typische Situation, die eine neue Lösung erfordert. Oft<br />
wird hier ein ganz konkretes Problem beschrieben, anhand dessen das<br />
einzuführende Muster ausprobiert werden kann.<br />
q Lösungsidee<br />
skizziert, wie das Muster aussehen könnte, mit dem das im vorigen<br />
Abschnitt beschriebene Problem gelöst werden könnte.<br />
q Struktur<br />
wird in einem UML Diagramm angegeben, das die statische Struktur der<br />
beteiligten Klassen verdeutlicht.<br />
q Die Beteiligten<br />
iTec<br />
Hier werden die beteiligten Klassen noch einmal in Worten aufgezählt<br />
19<br />
und ihre Rollen ausführlicher beschrieben.
Patternbeschreibung 11. Patterns und Frameworks<br />
q Zusammenspiel<br />
erklärt die Kooperation zwischen den beteiligten Klassen. Dieser Abschnitt<br />
enthält oft auch ein (UML) Diagramm, in dem der Austausch von Nachrichten<br />
zwischen Objekten verdeutlicht wird.<br />
q Anwendbarkeit<br />
beschreibt Situationen, in denen das Muster sinnvoll eingesetzt werden kann,<br />
bzw. die Bedingungen, die gelten müssen, damit es eingesetzt werden kann.<br />
q Folgen<br />
beschreibt die Vor- und Nachteile, die dieses Muster mit sich bringt.<br />
q Implementation<br />
diskutiert Aspekte, die bei Implementation des Musters zu bedenken sind<br />
q Codebeispiele<br />
gibt konkrete Implementationen des Musters an anhand eines typischen<br />
Problems, in den wichtigsten OO-Sprachen (JAVA, C++)<br />
q Bekannte Anwendungen (bekannt!)<br />
q Verwandte Muster<br />
geht auf Beziehungen zwischen diversen Mustern ein; es ist selten so, daß<br />
eine Problemklasse nur durch ein einziges Muster allein entschärft wird.<br />
20<br />
iTec
Proxy 11. Patterns und Frameworks<br />
q Name<br />
Proxy<br />
q Absicht<br />
stelle ein Stellvertreter/Platzhalter für ein Objekt zur Verfügung, um<br />
über dieses Platzhalter-Objekt Zugriff auf das ursprüngliche Objekt<br />
zu garantieren<br />
q Problem<br />
manchmal ist die Erzeugung und Initialisierung eines Objektes<br />
aufwendig – grosse Datenmenge (z.B. Bilddaten), oder "entlegener"<br />
Ort wo das Objekt gespeichert ist (Netz, verteilte DB), oder grosse<br />
Anzahl von Objekten bei Initialisierung einer komplexen Anwendung<br />
q Lösungsidee verschiebe die Objektinitialisierung bis zum eigentlichen<br />
Gebrauch seitens des Verwenders (Klient), und stelle ihm solange nur<br />
ein Stellvertreterobjekt zur Verfügung (Schnittstelle mit den<br />
Operationen des eigentlichen Objekts).<br />
Der Klient soll den Proxy so sehen als ob er das richtige Objekt wäre.<br />
iTec<br />
21
Proxy 11. Patterns und Frameworks<br />
Struktur<br />
Klassendiagramm<br />
Klient<br />
Subjekt<br />
{abstract}<br />
operation()<br />
. . . ()<br />
iTec<br />
EchtesSubjekt<br />
operation()<br />
. . . ()<br />
Objektdiagramm<br />
einKlient:<br />
operation()<br />
vertritt<br />
einProxy:<br />
Proxy<br />
operation()<br />
. . . ()<br />
operation()<br />
operation() {<br />
vertritt.operation();<br />
}<br />
einEchtesSubjekt:<br />
22
Proxy 11. Patterns und Frameworks<br />
q Rollen<br />
Klient: greift auf ein Subjekt über ein Proxy zu, so als ob es<br />
ein EchtesSubjekt wäre. Er ist aber nur mit dem Proxy direkt<br />
verbunden (Referenz, Zeiger)<br />
Subjekt: stellt die gemeinsame Schnittstelle dar, sodass Proxy<br />
überall dort verwendet werden kann, wo eine EchtesSubjekt<br />
gebraucht wird. Hält meistens nur die Operationen (alle<br />
Attribute von EchtesSubjekt geschützt).<br />
Insbesondere stellt Subjekt sicher, dass Proxy und<br />
EchtesSubjekt gleich aussehen<br />
Proxy: ist der Ansprechpartner für Nachrichten eines<br />
Klients. Hält eine Referenz/Zeiger auf das EchteSubjekt, und<br />
ist verantwortlich für die Erzeugung (und evtl. Zerstörung) von<br />
EchtesSubjekt.<br />
iTec<br />
EchtesSubjekt:<br />
enthält die Daten und den Programmcode<br />
23
Proxy 11. Patterns und Frameworks<br />
Rollen (Fortsetzung)<br />
es gibt mehrere Typen von Proxy:<br />
q Remote-Proxy<br />
EchtesSubjekt (Objekte) befindet sich in einem anderen<br />
Adressraum (anderer Prozess, oder woanders im Netzwerk).<br />
Ein Remote -Proxy kodiert und schickt einen Request an<br />
EchtesSubjekt<br />
q Virtuelles Proxy<br />
erzeugt ein umfangreiches Objekt im gleichen Adressraum "auf<br />
Anfrage"<br />
q Schutz-Proxy<br />
kontrolliert den (geschützten) Zugriff auf EchtesSubjekt, z.B.<br />
über Zugriffsrechte<br />
iTec<br />
24
Proxy 11. Patterns und Frameworks<br />
Folgen<br />
q ein Remote-Proxy kann einem Klient verbergen, dass das angesprochene<br />
Objekt (EchtesSubjekt) in anderem Adressraum liegt<br />
q ein Virtuelles Proxy kann optimieren:<br />
– verzögerte Objektinitialisierung (vielleicht nie)<br />
– Lastausgleich bei initialisierungsintensiven Anwendungen<br />
q ein virtuelles Proxy kann selber einfache Daten halten<br />
iTec<br />
25
Proxy 11. Patterns und Frameworks<br />
iTec<br />
26
interface Subjekt {<br />
int getA();<br />
float getB();<br />
}<br />
class Proxy implements Subjekt {<br />
private EchtesSubjekt vertritt;<br />
// delegiere Methodenaufrufe an EchtesS:<br />
}<br />
Proxy 11. Patterns und Frameworks<br />
Beispiel in Java: virtueller Proxy<br />
public int getA() {<br />
iTec<br />
return getEchtesSubjekt().getA(); }<br />
public float getB() {<br />
return getEchtesSubjekt().getB(); }<br />
protected EchtesSubjekt getEchtesSubjekt() {<br />
if ( vertritt == null )<br />
vertritt = new EchtesSubjekt();<br />
return vertritt;<br />
}<br />
lazy lazy initialization<br />
class EchtesSubjekt implements Subjekt {<br />
protected int a;<br />
protected int b;<br />
public int getA() {<br />
System.out.println("EchtesSub.getA()");<br />
return a; }<br />
public float getB() {<br />
System.out.println(" EchtesSub.getB()");<br />
return b; }<br />
public EchtesSubjekt() {a=1; b=1;}<br />
}<br />
public class Klient {<br />
public static void main(String args[]) {<br />
Proxy p = new Proxy();<br />
System.out.println(" a: " + p.getA() );<br />
System.out.println(" b: " + p.getB() );<br />
}<br />
}<br />
27
Zustand/State 11. Patterns und Frameworks<br />
q Name<br />
Zustand/State<br />
q Absicht<br />
lass ein Objekt sein Verhalten ändern (Ausführung einer<br />
Operation) wenn sein interner Zustand sich ändert<br />
q Problem ein Objekt hat interne Zustände (Attributwerte), die<br />
einige seiner Operationen beeinflussen. z.B. Datei öffnen:<br />
Datei<br />
dateiname : String<br />
offen : boolean<br />
oeffnen()<br />
schliessen()<br />
weitereOp()<br />
Spezialisierung<br />
Datei<br />
dateiname : String<br />
oeffnen()<br />
schliessen()<br />
weitereOp()<br />
Problem: ein Dateiobjekt müsste<br />
seine Klasse wechseln können<br />
iTec<br />
OffeneDatei<br />
oeffnen()<br />
schliessen()<br />
GeschlosseneDatei<br />
oeffnen()<br />
schliessen()<br />
28
Zustand/State 11. Patterns und Frameworks<br />
q Lösungsidee<br />
lagere den zustandsabhängigen Teil aus der Klasse aus (eigene<br />
Klasse), implementiere in der Klasse nur den zustandsunabhängigen<br />
Teil, und delegiere zustandsabhängige Operationen an die<br />
ausgelagerte Klasse<br />
q Struktur<br />
operation() {<br />
state.opAbhängig();<br />
}<br />
KlasseMitZustand<br />
opAbhängig()<br />
opUnabhängig()<br />
1<br />
state<br />
Zustand<br />
{abstract}<br />
opAbhängig()<br />
ZustandA<br />
opAbhängig()<br />
ZustandB<br />
opAbhängig()<br />
. . .<br />
Zustandsänderung: - wie wird er geändert ?<br />
- wer bzw. wo wird ein Zustand geändert ?<br />
iTec<br />
29
Zustand/State 11. Patterns und Frameworks<br />
Rollen<br />
q KlasseMitZustand<br />
die eigentliche Klasse, die nur den zustandsunabhängigen Teil<br />
enthält (evtl. mit Operationen die den Zustand verändern). Sie<br />
hält eine Referenz auf den aktuellen Zustand, über die<br />
zustandsabhängige Operationen delegiert werden.<br />
q Zustand<br />
abstrakte Schnittstelle (Polymorphismus) für die delegierten,<br />
zustandsabhängigen Operationen<br />
q konkreter Zustand (ZustandA, ZustandB)<br />
implementieren die zustandsabhängigen Operationen<br />
iTec<br />
Zustandsänderungen:<br />
ersetze aktuell referenziertes Zustandsobjekt durch ein neues<br />
30
Zustand/State 11. Patterns und Frameworks<br />
Anwendbarkeit:<br />
1. wenn ein Objekt Operationen hat, deren Logik von einem<br />
Zustand des Objektes selber abhängt der sich zur Laufzeit<br />
ändern kann<br />
2. das Objekt wenig Zustände hat und die Zustandsübergänge<br />
selber einen einfache Zustandsübergangsgraphen besitzen<br />
3. falls 2 nicht erfüllt ist, empfiehlt sich eine Realisierung mit<br />
einem endlichen Zustandsautomat<br />
iTec<br />
31
Zustand/State 11. Patterns und Frameworks<br />
wenn man komplexe Zustandsübergänge hat, benutzt man vorzugsweise<br />
einen endlichen Zustandsautomaten:<br />
iTec<br />
Zustand<br />
z_i<br />
Zustand<br />
z_j<br />
Ereignis e_k<br />
/<br />
Aktion a_m<br />
im im Zustand z_j z_j führt führt<br />
das das Ereignis e_k e_k zum zum<br />
Folgezustand z_j z_j und und<br />
löst löst Akton Akton a_m a_m aus aus<br />
Zustand<br />
Ereignis<br />
e_1<br />
e_2<br />
. . .<br />
e_k<br />
. . .<br />
z_1 z_2 . . . z_i . . .<br />
(folgezustand,<br />
aktion)<br />
Darstellung in in einer einer<br />
Tabelle von von Paaren:<br />
(folgezustand,aktion)<br />
( z_j ,<br />
a_m )<br />
32
Zustand/State 11. Patterns und Frameworks<br />
ComplexClass<br />
...<br />
actState : State<br />
act : Action;<br />
... event1(..) {<br />
//operation op1 implements event 1<br />
actState = hasSTT.transit(actState,<br />
E1, act );<br />
switch (act) {<br />
case A1: action1(..); //execute action<br />
break;<br />
case A2: action2(..);<br />
. . .<br />
}<br />
event2(..)<br />
...<br />
action1(..) //action A1<br />
action2(..) //action A2<br />
. . .<br />
iTec<br />
hasSTT<br />
1..* 1<br />
stTable //state table<br />
StateTransitionTable<br />
create(ComplexClass cp) //loads table<br />
save()<br />
addState( State st )<br />
addEvent( Event ev )<br />
addTransition(State st1, State st2,<br />
Event ev, Action act)<br />
removeState(...)<br />
...<br />
State transit( State st, Event ev,<br />
Action &act)<br />
33
Zustand/State 11. Patterns und Frameworks<br />
Konsequenzen<br />
q es strukturiert das Objektverhalten nach Zuständen:<br />
– jede Zustandsklasse enthält die Zustandsbesonderheiten<br />
– leichter les- und vor allem wartbar<br />
q unterstützt Zustandsklassifizierung: Zustände sind manchmal<br />
klassifizierbar durch Super-/Subklassenbeziehungen<br />
q Zustände werden explizit gemacht<br />
iTec<br />
34
Beobachter/Observer 11. Patterns und Frameworks<br />
q Name: Beobachter (Observer)<br />
q Absicht<br />
Objekte können Daten bei einem Informationsanbieter<br />
abonnieren. Bei jeder Änderung der abonnierten Daten werden<br />
die Abonnenten automatisch über die Änderung informiert, die<br />
sich dann die geänderten Daten holen.<br />
q Problem<br />
voneinander abhängige Objekte sollen nicht zu stark aneinander<br />
gekoppelt werden, was Wiederverwendbarkeit einschränken<br />
würde.<br />
q Lösungsidee<br />
Trennung von Informationsbereitsteller und einer Menge von<br />
Informationsverarbeitern bzw. Darstellern der Information. Die<br />
Bereitsteller müssen die Verarbeiter nicht direkt kennen: statt<br />
direkter Kommunikation über Aufrufe benutze eine indirekte<br />
über Benachrichtigungen.<br />
iTec<br />
35
Beobachter/Observer 11. Patterns und Frameworks<br />
Struktur<br />
Subjekt<br />
{abstract}<br />
- beobachtetVon : ListOfBeobachter<br />
#benachrichtigen()<br />
+registrieren(beob:Beobachter)<br />
+löschen(beob:Beobachter)<br />
beobachtetVon ?<br />
1..* ? beobachtet<br />
für alle beo in beobachtetVon:<br />
beo.aktualisieren(. . . );<br />
*<br />
Beobachter<br />
{abstract}<br />
- beobachtet : Subjekt<br />
+aktualisieren(. . . )<br />
beobachtet.getMeineDaten();<br />
. . . //aktualisiere zustand<br />
Client<br />
setMeineDaten(x)<br />
MeinSubjekt<br />
-meineDaten : Data<br />
+getMeineDaten(): Data<br />
+setMeineDaten(d:Data)<br />
. . .<br />
. // bearbeite und<br />
. // aktualisiere<br />
. // meineDaten<br />
benachrichtigen();<br />
MeinBeobachter<br />
- zustand<br />
+aktualisieren(. . . )<br />
iTec<br />
36
Beobachter/Observer 11. Patterns und Frameworks<br />
Teilnehmer<br />
q Subjekt<br />
kennt seine Beobachter (beliebig viele) als abstrakte Beobachter;<br />
bietet Schnittstelle zur (Ent)Registrierung von Beobachtern;<br />
hat Operation zur Benachrichtigung registrierter Beobachter<br />
q Beobachter<br />
bietet einen Typ (abstrakte Klasse/Interface) der benachrichtigt<br />
werden kann von Subjekten<br />
q MeinSubjekt<br />
wird unabhängig von Beobachtern programmiert: hält aber Daten von<br />
denen Beobachter-Objekte abhängig sind;<br />
sendet (fast automatisch) Benachrichtigungen an Beobachter-Objekte<br />
wenn Daten sich ändern<br />
q MeinBeobachter<br />
ist abhängig und hält eine Referenz zu einem MeinSubjekt-Objekt;<br />
enthält Daten die mit Daten des Subjekts konsistent sein müssen;<br />
implementiert iTec die aktualisiere-Operation zur Benachrichtigung<br />
37
Beobachter/Observer 11. Patterns und Frameworks<br />
Zusammenspiel<br />
q jedes Objekt der Klasse Subjekt führt eine Liste von Beobachtern, welche<br />
an Veränderungen im Zustand dieses Objekts interessiert sind. registrieren<br />
bzw. loeschen fügt Beobachter in die Liste ein bzw. entfernt sie.<br />
q nach jeder Veränderung schickt das Subjekt sich selbst die Nachricht<br />
benachrichtigen. Diese iteriert die Liste der Beobachter und schickt jedem<br />
Beobachter die Nachricht aktualisieren. Ggf. können Informationen (z. B.<br />
die Art des eingetretenen Ereignisses, oder das benachrichtigende Objekt<br />
selber) als Parameter mitgegeben werden.<br />
q jeder benachrichtigte Beobachter reagiert, indem er beim benachrichtigenden<br />
Sujekt mit getXXX- Nachrichten die ihn interessierenden<br />
Informationen abruft.<br />
q alternativ können der aktualisiere-Nachricht die veränderten Daten gleich<br />
mitgegeben werden, wodurch der Abruf durch getXXX entfällt. Dieses<br />
"Bring"-Prinzip ist effizient, koppelt aber Subjekt und Beobachter stärker<br />
als das mit getXXX realisierte "Hol"-Prinzip.<br />
iTec<br />
38
Beobachter/Observer 11. Patterns und Frameworks<br />
Zusammenspiel<br />
setMeineDaten<br />
ms :MeinSubjekt<br />
registrieren<br />
mb<br />
registrieren<br />
db<br />
mb :MeinBeobachter<br />
db :DeinBeobachter<br />
benachrichtigen<br />
aktualisieren<br />
getMeineDaten<br />
Data<br />
aktualisieren<br />
getMeineDaten<br />
Data<br />
iTec<br />
39
Beobachter/Observer 11. Patterns und Frameworks<br />
Anwendbarkeit<br />
q wenn eine Änderung in einem Objekt Änderungen in anderen<br />
verlangt, und das erstere weiss zur Programmierzeit oder zum<br />
Systemstart nicht, welche anderen von ihm abhängen<br />
q wenn man also dynamische Abhängigkeiten hat<br />
q wenn man Abhängigkeiten in reinen Client/Server-Beziehungen<br />
modellieren möchte zwecks leichterer Änderbarkeit<br />
weiss von<br />
Clientklasse<br />
Serverklasse<br />
weiss nichts von<br />
iTec<br />
40
Beobachter/Observer 11. Patterns und Frameworks<br />
Folgen<br />
q schwache, einseitige Kopplung zwischen unabhängiger und<br />
abhängigen Klassen: erstere kennt nur eine abstrakte<br />
Schnittstelle zur Benachrichtigung<br />
q sauberer Design: Aktualisierungslogik im abhängigen Teil, nicht<br />
im bestimmenden (nur benachrichtigen, nicht aktualisieren)<br />
q dadurch können die beiden Klassentypen zu unterschiedlichen<br />
Systemebenen gehören (nächste Folie)<br />
q unterstützt eine Art Broadcasting<br />
iTec<br />
41
Beobachter/Observer 11. Patterns und Frameworks<br />
bekannte Anwendungen<br />
von Präsentationsschicht unabhängige Anwendungslogik<br />
Präsentationsschicht<br />
GUI-Komponenten GUI-Komponenten repräsentieren<br />
repräsentieren<br />
Daten Daten der der Anwendungslogikschicht<br />
Anwendungslogikschicht<br />
GUI-Komponente1<br />
GUI-Komponente2<br />
GUI-Komponente3<br />
Klasse1<br />
Anwendungslogikschicht<br />
Klasse2<br />
Klassen Klassen der der Anwendungslogikschicht<br />
Anwendungslogikschicht<br />
verarbeiten verarbeiten Daten, Daten, ohne ohne sie sie am am Bildschirschirm<br />
zu zu präsentieren<br />
Bild-<br />
präsentieren<br />
iTec<br />
Abhängigkeit<br />
42
Beobachter/Observer 11. Patterns und Frameworks<br />
Implementation<br />
q wann soll die Registrierung (Löschen) geschehen?<br />
meistens im Konstruktor (Destruktor) eines Beobachters<br />
q mehrere Subjekte: wie erkennen, welches Subjekt benachrichtigt?<br />
Parameter des Subjektes (this) bei Benachrichtigung übergeben<br />
q muss immer gleich aktualisiert werden?<br />
nein, man kann eine lazy-computing Technik einsetzen<br />
iTec<br />
43
Beobachter/Observer 11. Patterns und Frameworks<br />
lazy computing: erst dann berechnen, wenn Daten gebraucht werden<br />
Client<br />
getMeinAttribut()<br />
MeinBeobachter<br />
-meinAttribut: Typ //abhängiges Attr.<br />
-meinAttributStatus //valid/invalid<br />
+benachrichtige()<br />
+getMeinAttribut(): Typ<br />
void benachrichtige() {<br />
meinAttributStatus = invalid;<br />
}<br />
Typ getMeinAttribut() {<br />
if (meinAttributStatus == invalid)<br />
{<br />
beobachtet.getMeineDaten();<br />
. . . //Neuberechnung von meinAttribut<br />
meinAttributStatus = valid;<br />
}<br />
return meinAttribut; }<br />
iTec<br />
Vorteil:<br />
weniger Berechnungen, falls Subjekt<br />
wesentlich öfter benachrichtigt als<br />
Beobachter befragt wird<br />
44
Beobachter/Observer<br />
public class MeinSubjekt<br />
extends Subjekt {<br />
private int wert;<br />
public MeinSubjekt( int wert ) {<br />
this.wert = wert;<br />
}<br />
public void setWert( int wert )<br />
{<br />
this.wert = wert;<br />
benachrichtigen();<br />
}<br />
public int getWert() {<br />
return wert;<br />
}<br />
}<br />
iTec<br />
4. Patterns und Frameworks<br />
public abstract class Subje kt {<br />
}<br />
private Beobachter[] beobachtetVon =<br />
new Beobachter[MAXB];<br />
private int anzBeobachter = 0;<br />
public void registrieren( Beobachter beob ) {<br />
beobachtetVon[ anzBeobachter++] = beob;<br />
}<br />
public void löschen( Beobachter beob ) {<br />
for ( int i = 0; i < anzBeobachter; ++i ) {<br />
if (beobachtetVon[i] == beob) {<br />
- - observerCnt;<br />
for ( ; i < anzBeobachter; ++i)<br />
beobachtetVon[ i ] = beobachtetVon[ i + 1 ];<br />
break;<br />
}<br />
}<br />
}<br />
public void benachrichtigen() {<br />
for (int i = 0; i < anzBeobachter; ++i)<br />
beobachtetVon[i].aktualisieren( this );<br />
}<br />
45
Beobachter/Observer 11. Patterns und Frameworks<br />
interface Beobachter {<br />
public void aktualisieren( Subje kt subjekt );<br />
}<br />
public class MeinBeobachter<br />
implements Beobachter {<br />
int abhgWert; //abhängiger Wert<br />
public class Anwendung {<br />
public static void main( String[] args )<br />
MeinSubjekt sub = new MeinSubjekt( 99 );<br />
MeinBeobachter beo =<br />
new MeinBeobachter( sub );<br />
}<br />
public MeinBeobachter( Subjekt subjekt ) {<br />
abhgWert = subjekt.getWert() / 2;<br />
subjekt.registrieren( this );<br />
}<br />
public void aktualisieren( Subjekt subjekt ) {<br />
abhgWert = subjekt.getWert() / 2;<br />
//zum Beispiel<br />
}<br />
public int getAbhgWert() {<br />
return abhgWert;<br />
}<br />
iTec<br />
}<br />
}<br />
sub.setWert( 2 );<br />
System.out.println( "abhgWert: " +<br />
beo.getAbhgWert() );<br />
hier hier wird wird im im Hintergrund der der<br />
abhängige Wert Wert von von<br />
MeinBeobachter aktualisiert.<br />
Ausgabe: abhgWert: 11<br />
46
Adapter 11. Patterns und Frameworks<br />
q Name: Adapter<br />
q Absicht<br />
erlaubt die Zusammenarbeit von Klassen die unterschiedliche Schnittstellen<br />
haben. Adapter wird als verbindendes Element zwischen die<br />
beiden Klassen eingefügt.<br />
q Problem<br />
Schnittstellenproblem: eine Klasse "Verwender" (Klient) soll eine andere<br />
Klasse "Dienstanbieter" (Ziel) verwenden. Der Verwender kann jedoch<br />
auf den Dienstanbieter nicht zugreifen, da der Verwender eine andere<br />
Schnittstelle erwartet als die, die vom Dienstanbieter angeboten wird.<br />
q Lösungsidee<br />
Der Dienstanbieter wird in einen Adapter eingepackt. Der Adapter<br />
bietet die Schnittstelle an, die der Verwender benötigt.<br />
Dazu gibt es zwei Varianten:<br />
• Objektadapter<br />
• Klassenadapter<br />
iTec<br />
Adapter<br />
47
Adapter 11. Patterns und Frameworks<br />
Struktur: Objektadapter<br />
Klient<br />
benutzt<br />
Ziel<br />
operation()<br />
adaptiert<br />
AdaptierteKlasse<br />
spezifischeOperation()<br />
Adapter<br />
operation()<br />
operation()<br />
. . .<br />
adaptiert.spezifischeOperation()<br />
. . .<br />
Adaption:<br />
q andere Operationensignatur – Operations- und Parameternamen,<br />
Parametertypen und -reihenfolge<br />
q andere Operationsfunktionen – ähnliche, aber nicht genau gleiche<br />
48<br />
iTec<br />
Semantik
Adapter 11. Patterns und Frameworks<br />
Adapter Beispiel:<br />
ein Zeicheneditor mit fremder Komponente TextAnzeige<br />
1 manipuliert *<br />
Zeicheneditor<br />
zeichne()<br />
begrenzungsrahmen()<br />
GraphischesObjekt<br />
{abstract}<br />
begrenzungsRahmen()<br />
zeichne()<br />
TextAnzeige<br />
getUrsprung()<br />
getHoehe()<br />
getBreite()<br />
zeigMich()<br />
Linie<br />
begrenzungsRahmen()<br />
zeichne()<br />
Kreis<br />
begrenzungsRahmen()<br />
zeichne()<br />
iTec<br />
TextAnzeige passt nicht zu GraphischesObjekt:<br />
Problem für den Zeicheneditor<br />
49
Adapter 11. Patterns und Frameworks<br />
Adapter Beispiel: ein Zeicheneditor<br />
mit fremder Komponente TextAnzeige<br />
1 manipuliert *<br />
Zeicheneditor<br />
zeichne()<br />
begrenzungsrahmen()<br />
GraphischesObjekt<br />
{abstract}<br />
begrenzungsrahmen()<br />
zeichne()<br />
einseitige Beziehung:<br />
TextAnzeige auch auch ohne ohne<br />
Quellcode verwendbar<br />
1<br />
text<br />
TextAnzeige<br />
getUrsprung()<br />
getHoehe()<br />
getBreite()<br />
zeigMich()<br />
Linie<br />
begrenzungsrahmen()<br />
zeichne()<br />
Kreis<br />
begrenzungsrahmen()<br />
zeichne()<br />
Text<br />
begrenzungsrahmen()<br />
zeichne()<br />
1<br />
iTec<br />
begrenzungsrahmen()<br />
text.getHoehe()<br />
text.getBreite()<br />
. . .<br />
zeichne()<br />
. . .<br />
text.zeigMich()<br />
. . .<br />
50
Adapter 11. Patterns und Frameworks<br />
Struktur: Klassenadapter<br />
Klient<br />
Ziel<br />
operation()<br />
AdaptierteKlasse<br />
spezifischeOperation()<br />
Adapter<br />
operation()<br />
benutzt Mehrfachvererbung<br />
operation()<br />
. . .<br />
spezifischeOperation()<br />
. . .<br />
iTec<br />
51
Adapter 11. Patterns und Frameworks<br />
Folgen<br />
q Klassenadapter<br />
benutzt nur eine Objekt, während Objektadapter aus zwei<br />
bestehen<br />
q Objektadapter<br />
kann auch Objekte von Unterklassen der adaptierten Klasse<br />
adaptieren, was der Klassenadapter nicht kann<br />
iTec<br />
52
Adapter 11. Patterns und Frameworks<br />
Implementation in JAVA: Objektadapter mit einem Interface<br />
class WasIchHabe {<br />
public void g() {}<br />
public void h() {}<br />
}<br />
interface WasIchMoechte {<br />
void f();<br />
}<br />
class WasIchBenutze {<br />
public void op(WasIchMoechte wim) {<br />
wim.f();<br />
}<br />
}<br />
class Adapter implements WasIchMoechte {<br />
//Referenz auf was adaptiert werden soll<br />
private WasIchHabe wasichhabe;<br />
//Konstruktor verbindet Adapter mit WasIchHabe<br />
public Adapter(WasIchHabe wih) {<br />
wasichhabe = wih;<br />
}<br />
public void f() {<br />
// Implementiert Methode mit den<br />
// Methoden in WasIchHabe:<br />
wasichhabe.g();<br />
wasichhabe.h();<br />
}<br />
}<br />
WasIchBenutze WasIchMoechte WasIchHabe<br />
iTec<br />
Adapter<br />
53
Kompositum 11. Patterns und Frameworks<br />
Zweck<br />
Füge Objekte zu Baumstrukturen zusammen, um Teil-Ganzes-<br />
Hierarchien zu repräsentieren.<br />
Das Kompositionsmuster ermöglicht es dem Klienten (Benutzer<br />
dieses Musters), sowohl einzelne Objekte als auch Kompositionen<br />
von Objekten einheitlich zu behandeln.<br />
Motivation<br />
viel verwendet in Grafik-Anwendungen<br />
und im Produktionsbereich (Produktbäume)<br />
iTec<br />
54
Kompositum 11. Patterns und Frameworks<br />
Beispiel:<br />
Grafikanwendung<br />
Grafik<br />
zeichne()<br />
hinzufügen(Grafik)<br />
entfernen(Grafik)<br />
holeNächstesKind()<br />
{abstract}<br />
*<br />
enthält<br />
iTec<br />
Linie<br />
zeichne()<br />
Rechteck Text<br />
zeichne() zeichne()<br />
für für alle alle g in in "enthält":<br />
g.zeichne();<br />
Bild<br />
zeichne() //Delegation!!<br />
hinzufügen(Grafik g)<br />
entfernen(Grafik)<br />
holeNächstesKind()<br />
füge füge g g ein ein in in<br />
"enthält" "enthält"<br />
55
Kompositum 11. Patterns und Frameworks<br />
iTec<br />
56
Kompositum 11. Patterns und Frameworks<br />
Komponente<br />
• Deklariert die Schnittstelle für Objekte in der zusammengefügten Struktur.<br />
• Implementiert, sofern angebracht, ein Defaultverhalten für die allen Klassen gemeinsame<br />
Schnittstelle (ist in JAVA dann abstrakte Klasse).<br />
• Deklariert eine Schnittstelle zum Zugriff auf und zur Verwaltung von Kindobjekten.<br />
• Definiert optional eine Schnittstelle zum Zugriff auf das Elternobjekt einer Komponente<br />
innerhalb der rekursiven Struktur und implementiert sie, falls dies angebracht erscheint.<br />
Blatt<br />
• Repräsentiert Blattobjekte in der Komposition. Ein Blatt besitzt keine Kindobjekte.<br />
• Definiert Verhalten für die primitiven Objekte in der Komposition.<br />
Kompositum<br />
• Definiert Verhalten für Komponenten, die Kindobjekte haben können.<br />
• Speichert Kindobjektkomponenten.<br />
• Implementiert kindobjekt-bezogene Operationen der Schnittstelle von Komponente.<br />
Klient<br />
Rollen<br />
57<br />
• Manipuliert iTec die Objekte in der Komposition durch die Schnittstelle von Komponente.
Kompositum 11. Patterns und Frameworks<br />
Verwendung<br />
Verwende das Kompositionsmuster, wenn<br />
• Teil-Ganzes-Hierarchien von Objekten repräsentiert werden sollen<br />
• KKKlienten in der Lage sein sollen, die Unterschiede zwischen<br />
zusammengesetzten und einzelnen Objekten zu ignorieren.<br />
Klienten behandeln alle Objekte in der zusammengesetzten Struktur<br />
einheitlich.<br />
iTec<br />
58
Singleton/Kompositum 11. Patterns und Frameworks<br />
Beispiel<br />
ein binärer Suchbaum: Entwurf als Kompositum und mit leerem<br />
Baum als Singleton<br />
q gewünscht: eine Klasse eines binären Suchbaums, dessen<br />
Objekte auch leer sein können: Binärbaum ohne Elemente<br />
q Anforderung: der leere Baum muss genauso ansprechbar sein<br />
wie ein nichtleerer binärer Suchbaum: also mit Operationen<br />
– isEmpty(), isIn(Object o), insert(Object o)<br />
q ein leerer Baum kann also nicht als Nullreferenz realisiert<br />
werden !<br />
iTec<br />
59
Singleton/Kompositum 11. Patterns und Frameworks<br />
ein binärer Suchbaum:<br />
Entwurf als Kompositum<br />
und mit leerem Baum<br />
als Singleton<br />
«Interface»<br />
BinSearchTree<br />
isEmpty(): boolean<br />
isIn(Object o): boolean<br />
insert(Object o): BinSearchTree<br />
0..2<br />
hasFather<br />
hasChild<br />
iTec<br />
«Singleton»<br />
EmptyTree<br />
empty: EmptyTree<br />
isEmpty(): boolean<br />
isIn(Object o): boolean<br />
insert(Object o): BinSearchTree<br />
Node<br />
info: Object<br />
leftChild: BinSearchTree<br />
rightChild: BinSearchTree<br />
isEmpty(): boolean<br />
isIn(Object o): boolean<br />
insert(Object o): BinSearchTree<br />
0..1<br />
60
Singleton/Kompositum 11. Patterns und Frameworks<br />
ein binärer Suchbaum: Entwurf als Kompositum und mit leerem<br />
Baum als Singleton<br />
:ClientClass<br />
empty:EmptyTree<br />
b =<br />
EmptyTree.Instance()<br />
Instance()<br />
b = b.insert(o1)<br />
b = b.insert(o2)<br />
b = b.insert(o3)<br />
insert(o1)<br />
insert(o2)<br />
insert(o3)<br />
new(o1)<br />
b:Node<br />
{b.info < o1}<br />
{b.info > o2}<br />
new(o2)<br />
new(o3)<br />
b.leftChild:Node<br />
b.rightChild:Node<br />
iTec<br />
61
Singleton/Kompositum 11. Patterns und Frameworks<br />
ein binärer Suchbaum:<br />
JAVA Code<br />
einzige einzige Instanz Instanz als als<br />
Klassenattribut<br />
Klassenattribut<br />
alle alle Konstruktoren<br />
Konstruktoren<br />
sind sind damit damit privat privat<br />
public interface BinSearchTree {<br />
boolean isEmpty();<br />
boolean isIn(Object e);<br />
BinSearchTree insert(Object e);<br />
}<br />
//----------------------------------------------------------------<br />
public class EmptyTree implements BinSearchTree {<br />
private static EmptyTree empty = new EmptyTree();<br />
private EmptyTree() {}<br />
Benutzung: Benutzung:<br />
Instance() Instance() als als "Konstruktor"<br />
"Konstruktor"<br />
gibt gibt einziges einziges empty empty als als<br />
EmptyTree-Objekt EmptyTree-Objekt heraus heraus<br />
hier hier wird wird aus aus dem dem einzigen einzigen<br />
EmptyTree-Objekt EmptyTree-Objekt jeweils jeweils<br />
ein ein neuer neuer Baum Baum erzeugt erzeugt der der<br />
nur nur aus<br />
iTec aus einer einer Wurzel Wurzel besteht besteht<br />
public static EmptyTree Instance() {<br />
return empty;<br />
}<br />
public boolean isEmpty() { return true; }<br />
public boolean isIn(Object e) { return false; }<br />
public BinSearchTree insert(Object e) {<br />
return new Node(e);<br />
}<br />
}<br />
62
Singleton/Kompositum 11. Patterns und Frameworks<br />
public class Node implements BinSearchTree {<br />
protected Object info;<br />
protected BinSearchTree left;<br />
protected BinSearchTree right;<br />
public Node(Object info) {<br />
}<br />
this.info = info;<br />
left = EmptyTree.Instance();<br />
right = EmptyTree.Instance();<br />
public boolean isEmpty() { return false; }<br />
public boolean isIn(Object e) {<br />
switch ( info.compareTo(e) ) {<br />
case -1: return left.isIn(e);<br />
case 0: return true;<br />
case +1: return right.isIn(e);<br />
}<br />
return false;<br />
} iTec<br />
Kompositum: Kompositum:<br />
füge füge leere leere Bäume Bäume<br />
als als Blätter Blätter ein ein<br />
public BinSearchTree insert(Object e) {<br />
switch ( info.compareTo(e) ) {<br />
case -1:<br />
left = left.insert(e);<br />
break;<br />
case 0: break;<br />
case +1:<br />
right = right.insert(e);<br />
break;<br />
}<br />
return this;<br />
}<br />
}<br />
63
Singleton/Kompositum 11. Patterns und Frameworks<br />
Benutzung des binären Suchbaumes<br />
public static void main( String args[ ] ) {<br />
}<br />
BinSearchTree tree = EmptyTree.Instance();<br />
tree = readTree( tree, "dateiname" );<br />
. . .<br />
public BinSearchTree readTree( BinSearchTree bst, String datei ) {<br />
int wert;<br />
// in einer Schleife werte einlesen<br />
. . .<br />
bst = bst.insert( Integer(wert) ); Achtung: Achtung: falls falls ein ein leerer leerer Baum Baum<br />
. . .<br />
unser unser Binärbaum Binärbaum<br />
} hat hat allgemeine allgemeine<br />
Knoten, Knoten, die die Informationen Informationen vom vom<br />
Typ Typ Object Object halten halten (keine (keine Templates!) Templates!)<br />
Deshalb: Deshalb: Wrapperklasse Wrapperklasse um um den den<br />
Integerwert! Integerwert!<br />
iTec<br />
übergeben übergeben wird, wird, ändert ändert sich sich<br />
die die Objektreferenz Objektreferenz durch durch die die<br />
Einfügung Einfügung des des ersten ersten Knotens Knotens<br />
64
Kompositum 11. Patterns und Frameworks<br />
wichtig beim Kompositum ist die Unterscheidung von Klassen- und<br />
Objektbäumen – das Kompositum generiert Objektbäume<br />
Klasse1<br />
objA<br />
Klasse11<br />
Klasse12<br />
objB<br />
objC<br />
objD<br />
objE<br />
objF<br />
Klasse121<br />
Klasse122<br />
q Klassenbäume sind statische Vererbungshierarchien<br />
q Objektbäume sind dynamisch erzeugte Bäume mit Knotenobjekten<br />
iTec<br />
65
Dekorierer 11. Patterns und Frameworks<br />
q Name: Dekorierer (decorator oder wrapper)<br />
q Absicht: füge einem Objekt dynamisch Zusatzfunktionalität hinzu<br />
q Problem: wenn man zu einer Basisfunktionalität wie Grafikdarstellung<br />
oder einer Ein-/Ausgabe vielfache Zusatzfunktionalität wie<br />
in einem Baukasten frei kombinierbar hinzufügen möchte, ergibt<br />
sich rasch eine kombinatorische Vielfalt, die man nur noch schwer in<br />
einer statischen Klassenstruktur darstellen und warten kann.<br />
q Lösungsidee: kombiniere die Zusatzfunktionalität dynamisch in einer<br />
Umhüllung in mehreren Schichten (Wrapper) um das ursprüngliche<br />
Objekt herum. Jede Hülle (Dekoriererklasse) kann genauso<br />
angesprochen werden wie das ursprüngliche Objekt<br />
iTec<br />
66
Dekorierer 11. Patterns und Frameworks<br />
Struktur<br />
Komponente<br />
{abstract}<br />
+operation()<br />
1<br />
KonkreteKomponente<br />
Dekorierer<br />
hatKomponente<br />
{abstract}<br />
+operation()<br />
+operation()<br />
operation() {<br />
zusatzoperationA();<br />
//sende operation weiter:<br />
hatKomponente.operation();<br />
}<br />
KonkreterDekoriererA<br />
+operation()<br />
#zusatzOperationA()<br />
KonkreterDekoriererB<br />
+operation()<br />
#zusatzOperationB()<br />
. . .<br />
iTec<br />
67
Dekorierer 11. Patterns und Frameworks<br />
Rollen<br />
q Komponente:<br />
die Schnittstelle für einen Klienten zum Gebrauch eines<br />
dekorierten Objektes<br />
q KonkreteKomponente:<br />
Objekt, das eine Basisfunktionalität bereitstellt welche mit<br />
Zusatzfunktionalität dekoriert werden kann<br />
q Dekorierer:<br />
enthält eine Referenz zu einer weiteren Komponente auf der<br />
die Operation weitergesandt wird<br />
q KonkreterDekorierer:<br />
fügt Zusatzfunktionalität zur Operation der<br />
KonkretenKomponente hinzu (dekoriert)<br />
iTec<br />
68
Dekorierer 11. Patterns und Frameworks<br />
Zusammenspiel<br />
dA:KonkreterDekoriererA<br />
hatKomponente<br />
dB:KonkreterDekoriererB<br />
hatKomponente<br />
kp:KonkreteKomponente<br />
operation()<br />
zusatzOperationA()<br />
operation()<br />
zusatzOperationB()<br />
operation()<br />
iTec<br />
69
Dekorierer 11. Patterns und Frameworks<br />
Beispiel<br />
. . .<br />
/*display text*/<br />
. . .<br />
Component<br />
{abstract}<br />
+draw()<br />
1<br />
hasComponent<br />
TextView<br />
+draw()<br />
Decorator<br />
{abstract}<br />
+draw()<br />
hasComponent. draw()<br />
scrollDraw()<br />
hasComponent. draw()<br />
borderDraw()<br />
BorderDecorator<br />
+draw()<br />
#borderDraw()<br />
ScrollDecorator<br />
+draw()<br />
#scrollDraw()<br />
BW 1<br />
:BorderDecorator<br />
SW 1<br />
:ScrollDecorator<br />
T 1<br />
:TextView<br />
draw()<br />
iTec<br />
draw()<br />
draw()<br />
70
Dekorierer 11. Patterns und Frameworks<br />
Konsequenzen<br />
q die Verbindung der Komponenten über hatKomponente wird im<br />
Konstruktor erzeugt:<br />
KonkreteKomponente kp<br />
= new KonkreteKomponente();<br />
KonkreterDekoriererA kdA = new KonkreterDekoriererA( kp ); //kdA mit kp verbunden<br />
KonkreterDekoriererB kdB = new KonkreterDekoriererB( kdA ); //kdB " kdA "<br />
. . .<br />
kdB.operation();<br />
oder:<br />
KonkreterDekoriererB kdB = new KonkreterDekoriererB(<br />
iTec<br />
KonkreterDekoriererB<br />
KonkreterDekoriererA<br />
KonkréteKomponente<br />
new KonkreterDekoriererA(<br />
new KonkreteKomponente( ) ) );<br />
71
Dekorierer 11. Patterns und Frameworks<br />
Konsequenzen<br />
q das Muster erlaubt, alle Kombinationen von Zusatzfunktionalität<br />
in einer einfachen Klassenstruktur zu beschreiben.<br />
Eine explizite Beschreibung wäre sehr aufwendig.<br />
q es ist einfach, neue Zusatzfunktionalität einzufügen<br />
q Nachteil: das vom Klienten angesprochene Objekt ist ein<br />
Dekorierer, nicht das Hauptobjekt (KonkreteKomponente):<br />
alle Dekorierer müssen sämtliche Operationen des Hauptobjekts<br />
anbieten, auch wenn sie diese nicht dekorieren<br />
iTec<br />
allerdings können diese Operationen für alle Konkreten-<br />
Dekorierer in der (abstrakten) Superklasse Dekorierer mit<br />
einfacher Delegation an das Hauptobjekt implementiert werden<br />
(siehe nächste Folie)<br />
72
Dekorierer 11. Patterns und Frameworks<br />
nichtdekorierte Operationen<br />
werden einfach zur<br />
KonkretenKomponente<br />
durchgereicht<br />
Komponente<br />
{abstract}<br />
+operation1()<br />
+operation2()<br />
+operation3()<br />
1<br />
hatKomponente<br />
KonkreteKomponente<br />
+operation1()<br />
+operation2()<br />
+operation3()<br />
Dekorierer<br />
+operation1()<br />
+operation2()<br />
+operation3()<br />
{abstract}<br />
operation2() {<br />
hatKomponente.operation2();<br />
}<br />
operation3() {<br />
hatKomponente.operation3();<br />
}<br />
operation1() {<br />
zusatzoperationA();<br />
//sende operation weiter:<br />
hatKomponente.operation1();<br />
}<br />
iTec<br />
KonkreterDekoriererA<br />
+operation()<br />
#zusatzOperationA()<br />
KonkreterDekoriererB<br />
+operation()<br />
#zusatzOperationB()<br />
. . .<br />
73
Dekorierer 11. Patterns und Frameworks<br />
Zusammenspiel: Durchreichen nichtdekorierter Operationen<br />
dA:KonkreterDekoriererA<br />
hatKomponente<br />
dB:KonkreterDekoriererB<br />
hatKomponente<br />
kp:KonkreteKomponente<br />
operation3()<br />
operation3()<br />
operation3()<br />
iTec<br />
74
Objektfabriken 11. Patterns und Frameworks<br />
Problem:<br />
Polymorphismus in Vererbungshierarchien:<br />
man kann Spezialisierungen<br />
polymorph benutzen, und neue<br />
Spezialisierungen verlangen keine<br />
Änderung bei der Benutzung: schön!<br />
pPZ<br />
alles wird zu Polygon<br />
. . .<br />
void optimiereLayout( Polygon[] pP, int anzahl) {<br />
. . .<br />
for ( i=0; i < anzahl; i++ ) {<br />
f[i] = pP[i].berechneFlaeche();<br />
. . .<br />
pP[i].verschieben(dx, dy);<br />
pP[i].fuellen( rot );<br />
}<br />
. . .<br />
Aber:<br />
irgendwo muss man die Objekte<br />
erzeugen - und da muss man<br />
konkret werden. Diese Erzeugungen<br />
können weit verstreut sein - eine<br />
Änderung wird aufwendig und<br />
fehleranfällig<br />
iTec<br />
Polygon[] pPZ;<br />
. . .<br />
pPZ[k++] = new Dreieck(p1,p2,p3);<br />
. . .<br />
pPZ[k++] = new Rechteck(lo, ru);<br />
. . .<br />
pPZ[k++] = new Polygon(parray);<br />
. . .<br />
optimiereLayout( pPZ, k ) ;<br />
Änderungswunsch:<br />
Änderungswunsch:<br />
DreieckMitGrafik(<br />
DreieckMitGrafik(<br />
p1, p1, p2, p2, p3 p3 ))<br />
75
Objektfabriken 11. Patterns und Frameworks<br />
Lösungsidee:<br />
kann man nicht auch die Erzeugung kapseln - und dann sogar<br />
polymorph gestalten?<br />
Antwort:<br />
ja, und zwar in einer "Objekterzeugungsfabrik" - Object Factory<br />
erzeuge (gewünschtesObjekt )<br />
Objektfabrik<br />
neues Objekt<br />
erzeuge( Dreieck)<br />
FigurenFabrik<br />
neues Dreieck<br />
Fabrikmuster sind sind<br />
Erzeugungsmuster<br />
iTec<br />
new DreieckMitGrafik(...)<br />
76
Objektfabriken 11. Patterns und Frameworks<br />
abstract class Figur {<br />
public abstract void zeichne();<br />
public abstract void lösche();<br />
}<br />
public static Figur fabrik( String typ )<br />
throws FalscheFigurenErzeugung {<br />
if ( typ.equals("Kreis") ) return new Kreis();<br />
if ( typ.equals("Rechteck") ) return new Rechteck();<br />
throw new FalscheFigurenErzeugung( typ );<br />
}<br />
class Kreis extends Figur {<br />
Kreis() {} //Konstruktor mit Paketsichtbarkeit<br />
public void zeichne() {<br />
System.out.println("Kreis.zeichne");<br />
}<br />
public void lösche() {<br />
System.out.println("Kreis.loesche");<br />
}<br />
}<br />
class Rechteck extends Figur { . . .<br />
iTec<br />
1. Schritt:<br />
wie man die Erzeugung<br />
von Objekten kapselt<br />
77
Objektfabriken 11. Patterns und Frameworks<br />
public class FigurenFabrik1 {<br />
String figlist[] = { "Kreis", "Rechteck",<br />
"Rechteck", "Kreis", "Kreis", "Rechteck" };<br />
List figuren = new ArrayList();<br />
public void test() {<br />
try {<br />
for( int i = 0; i < figlist.length; i++)<br />
figuren.add( Figur.fabrik( figlist[i] ) );<br />
} catch( FalscheFigurenErzeugung e ) {<br />
System.out.println( e.getMessage() );<br />
}<br />
Iterator figIt = figuren.iterator();<br />
while( figIt.hasNext() ) {<br />
Figur f = (Figur)figIt.next();<br />
f.zeichne();<br />
f.lösche();<br />
}<br />
}<br />
public static void main(String args[]) {<br />
new FigurenFabrik1().test();<br />
}<br />
}<br />
iTec<br />
78
Objektfabriken 11. Patterns und Frameworks<br />
2. Schritt: wie man eine polymorphe Erzeugung bereitstellt:<br />
das Fabrikmuster<br />
Abhängigkeit (UML)<br />
abstrakte Produkt Meu fabrik(. fabrik(. . .) .{<br />
.) {<br />
abstrakte Klasse: Klasse:<br />
Product return return create(. create(. ...);<br />
Product fabrik(..) fabrik(..)<br />
.);<br />
nicht }<br />
nicht implementiert implementiert<br />
}<br />
Produkt<br />
operation1(..)<br />
operation2(..)<br />
. . .<br />
(Name)<br />
ProduktFabrik<br />
create(. . .): Produkt<br />
fabrik( . . . ): Produkt<br />
fabrik(..)<br />
Klient<br />
KonkretesProdukt<br />
KonkretesProdukt(..)<br />
operation1(..)<br />
operation2(..)<br />
. . .<br />
iTec<br />
(Konstruktor)<br />
KonkretesProduktFabrik<br />
create(. . .): Produkt<br />
Eine Anwendung benutzt die Produktfabrik, um<br />
Objekte zu erzeugen. Die KonkreteProduktFabrik<br />
erzeugt ein KonkretesProdukt, dessen Klasse für die<br />
Anwendung transparent ausgetauscht werden kann.<br />
79
Objektfabriken 11. Patterns und Frameworks<br />
Fabrik-Beispiel: eine Menufabrik<br />
Menu<br />
addOption(option:String)<br />
removeOption(option:String)<br />
select(): String<br />
. . .<br />
Menu Menu fabrik(String fabrik(String titel) titel) {{<br />
return return create(titel); create(titel);<br />
}}<br />
Menufabrik<br />
create(titel:String): Menu<br />
fabrik(titel:String): Menu<br />
iTec<br />
KonsoleMenu<br />
addOption(option:String)<br />
removeOption(<br />
option:String)<br />
select(): String<br />
. . .<br />
AWTMenu<br />
. . .<br />
KonsoleMenufabrik<br />
create(titel:String):<br />
Menu<br />
AWTMenufabrik<br />
create(titel:String):<br />
Menu<br />
80
Objektfabriken 11. Patterns und Frameworks<br />
das Fabrikmuster kapselt die Erzeugung von Objekten einer Klasse.<br />
Häufig sind aber Objekte mehrerer zusammenhängender Klassen in<br />
einer Fabrik zu erzeugen.<br />
das AbstrakteFabrik Muster<br />
erzeuge (gewünschtesObjekt1 )<br />
Objekt1fabrik<br />
neues Objekt1<br />
gemeinsame Basis<br />
(z.B. graphische Menuelemente ein<br />
bestimmtes Betriebssystem)<br />
erzeuge (gewünschtesObjekt2 )<br />
Objekt2fabrik<br />
neues Objekt2<br />
iTec<br />
81
Objektfabriken 11. Patterns und Frameworks<br />
AbstrakteFabrik: die Struktur<br />
iTec<br />
82
Objektfabriken 11. Patterns und Frameworks<br />
Anwendbarkeit von Abstrakten Fabriken<br />
q wenn man eine Anwendung braucht, die von der Erzeugung<br />
ihrer "Produkte" und deren Komposition/Aggregation<br />
unabhängig sein soll<br />
q wenn man Produktfamilien hat<br />
q im Design von Klassenbibliotheken<br />
da Polymorphismus einer der Pfeiler des objektorientierten Ansatzes ist,<br />
und eine polymorphe Objekterzeugung das Gegenstück zu polymorpher<br />
Benutzung von Objekten ist, ist das Fabrikmuster eines der am<br />
weitesten verbreiteten überhaupt!<br />
iTec<br />
83