Software Engineering 11. Entwurfsmuster III Franz-Josef Elmer, Universität Basel, HS 2012 Software Engineering: 11. Entwurfsmuster III 2 Entwurfsmuster: Composite – Compose objects into a tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. GoF(163) – Struktur: Client «Interface» Component operation() add(c:Component) remove(c:Component) getChild(index:int) children * 0..1 parent Leaf +operation() Universität Basel, HS 2012 Composite operation() add(c:Component) remove(c:Component) getChild(index:int) for each child do child.operation(); © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 3 Composite – Beteiligte Klassen: ● ● ● ● – Component: – Definiert ein Interface für alle Objekte des Baumes Leaf: – Repräsentiert ein Blatt im Baum Composite: – Definiert das Verhalten eines Knoten mit Kindern – Hält die Kindkomponenten – Implementiert alle Operation, die sich auf Kinder beziehen Client: – Manipuliert Objekte im Baum via dem Component Interface Zusammenarbeit: ● Der Client ruft für ein beliebiges Objekt im Baum operation() auf. Abhängig von der Objektklasse bewirkt operation() unterschiedliches. Im Fall eines Composite Objekts wird der Aufruf an alle Kinder weitergereicht. Eventuell werden davor und/oder danach noch zusätzliche Operationen ausgeführt. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 4 Composite – Anwendbar wenn ● – eine baumartige Struktur aus Objekten modelliert werden soll und der Unterschied zwischen Leaf und Composite in gewissen Kontexten ignoriert werden kann. Konsequenzen: ● ● Definiert eine Klassenhierarchie aus einfachen Objekten und zusammengesetzten Objekten. Vereinfacht den Client, da einfache und zusammengesetzte Objekt gleich behandelt werden. – ● ● Ausnahme: Manipulationen der Baumstruktur Es ist einfach neue Objekttypen hinzuzufügen (Open-Closed Prinzip). Nachteil: Manchmal zu Allgemein. Dies kann zu Typenüberprüfung zur Laufzeit führen. – Universität Basel, HS 2012 Beispiel: Eine neuer Typ hat eine spezielle Methode, die nicht im Component Interface deklariert ist. © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 5 Composite – Implementierung: ● Kindkomponente hat expliziten Zugriff auf Elternkomponente: «Interface» Component +getParent():Composite ... Component -parent:Composite +getParent():Composite +setParent(p:Composite) ... In den Implementierungen der Methoden add() und remove() von Composite muss dies entsprechend berücksichtigt werden. ● Die Kinder eines Composite-Objekts müssen nicht geordnet sein. – ● oder Konsequenz: Anderes Zugriffsinterface, z.B. getChildren():Component[*] statt getChild(index:int) Maximierung des Component Interfaces durch Hinzufügen von so vielen gemeinsamen Operationen wie möglich. – – Universität Basel, HS 2012 Vorteil: Client muss nicht den Typ der Komponente prüfen Nachteile: Operation machen für gewisse Typen keinen Sinn. © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 6 Composite: Implementierung – Deklaration der Methoden des Kinder Managements: ● im Component Interface: – – ● Vorteil: Einheitliches Interface aller Knoten im Baum Nachteile: ● Überprüfung des Knotentyps für Operationen wie Hinzufügen oder Wegnehmen eines Kindes. ● Fragwürdige Implementierungen dieser Operationen in den Leaf Klassen. in der Composite Klasse: – – Vorteil: Kinder Management nur dort wo es Sinn macht. Nachteil: Client Klasse muss bei Manipulationen des Baums auf Composite überprüfen und umwandeln. TreeUser «Interface» Component operation() children * 0..1 Composite TreeBuilder Universität Basel, HS 2012 operation() add(c:Component) remove(c:Component) getChild(index:int) © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 7 Composite: Beispiele – Java AWT Components: Component paint(g:Graphics) getParent():Container * 0..1 Container add(c:Component) remove(c:Component) getComponentCount():int getComponent(index:int) – W3C XML DOM API: «Interface» Node appendChild(c:Node):Node * getChildNodes():NodeList 0..1 «Interface» Element setAttributeNode(a:Attr):Attr getAttributeNode(name:String):Attr Universität Basel, HS 2012 0..1 if leaf throw DOMException «Interface» Attr * getName():Name getValue():String © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 8 Entwurfsmuster: Iterator – Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. GoF(257) – Struktur: «Interface» Aggregate createIterator():Iterator Client «Interface» Iterator first():Object next():Object current():Object isDone():boolean ConcreteAggregate createIterator():Iterator ConcreteIterator 1 return new ConcreteIterator(this); Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 9 Iterator – Beteiligte Klassen: ● ● ● ● – Iterator: – Definiert ein Interface für den sequentiellen Zugriff auf die Objekte einer Ansammlung von Objekten (engl.: aggregate, collection). ConcreteIterator: – Implementiert das Iterator Interface und hält die momentane Position des Zugriffs (durch current() abfragbar). Aggregator: – Definiert ein Interface zur Erzeugung eines Iterator Objekts. ConcreteAggregator: – Implementiert das Aggregator Interface und erzeugt eine Instanz einer passenden ConcreteIterator Klasse. Zusammenarbeit: ● ● ● Mit first() holte der Client das erste Objekt. Mit next() wird das jeweils darauf folgende Objekt geholt. Mittels isDone() kann der Client feststellen, ob es noch weiter Objekte gibt oder nicht. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 10 Iterator – Anwendbar wenn ● ● – auf die Objekte einer Ansammlung zugegriffen werden soll, ohne interne Repräsentation der Ansammlung preiszugeben ein einheitliches Interface zum Zugriff unterschiedlicher Typen von Ansammlungen benötigt wird. Konsequenzen: ● ● Konform mit dem Geheimnisprinzip. Unterschiedliche Iterator-Implementierungen erlauben eine Aggregatstruktur auf unterschiedliche Weise zu durchlaufen. – ● ● ● ● Beispiel: Baumstruktur kann entweder zuerst in die Tiefe (deep first) oder in Breite (breadth first) durchlaufen werden. Iterator vereinfacht das Aggregate Interface (nur eine Methode). Mehrere Iteratoren können parallel die Ansammlung durchlaufen. Die Operation zur Erzeugung eines Iterator ist eine Factory Methode. Problem: Was passiert wenn die Aggregatstruktur sich während der Iteration ändert? Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 11 Iterator – Implementierung: ● ● ● Statt der vier Operationen des Iterators (first, next, current, isDone) sind auch weniger möglich. Im einfachsten Fall gibt es nur eine Operation next() welche das nächste Objekt oder ein Null-Objekt liefert. Der Traversierungs Algorithmus ist entweder in der konkreten Iterator Klasse implementiert oder in der konkreten Aggregat Klasse. Im letztern Fall speichert der Iterator nur die momentane Position. Ein solcher Iterator wird auch Cursor genannt. In Java sind konkrete Iteratoren oft private innere Klassen der konkreten Aggregatoren. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 12 Iterator: Beispiele aus der Java API – Interface Enumeration: «Interface» Enumeration nextElement():Object hasMoreElements():boolean – Collection API: ● Aggregator ≙ Collection «Interface» Collection iterator():Iterator ... – Java Database Connection (JDBC) API: ● ● «Interface» Iterator next():Object hasNext():boolean remove() Aggregator ≙ Statement Iterator ≙ ResultSet «Interface» Statement executeQuery(sql:String):ResultSet ... Universität Basel, HS 2012 «Interface» ResultSet next():boolean getObject(index:int):Object ... © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 13 Entwurfsmuster: Proxy – Provide a surrogate or placeholder for another object to control access to it. GoF(207) – Struktur: Client «Interface» Subject operation() Proxy operation() -realSubject RealSubject 1 operation() ... realSubject.operation() ... Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 14 Proxy – Beteiligte Klassen: ● ● ● – Subject: – Gemeinsames Interface von RealSubject und Proxy. RealSubject: – Definiert reales Objekt welches durch das Proxy Objekt repräsentiert wird. Proxy: – Ersatz für das reale Objekt. – Delegiert den Methodenaufruf an das reale Objekt weiter. – Kontrolliert den Zugriff auf ein reales Objekt. Zusammenarbeit: ● ● Aufrufe der im Subject Interface definierten Methoden werden über das Proxy Objekt an das reale Objekt weitergeleitet. Das Proxy Objekt kann davor und/oder danach weitere Aktionen ausführen. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 15 Proxy – Anwendbar wenn ● ● ● ● – das reale Objekt zu Beginn noch nicht zur Verfügung steht das reale Objekt zur Laufzeit gewechselt werden soll, ohne dass der Client etwas davon mitbekommt das reale Objekt vor unberechtigtem Zugriff geschützt werden soll ganz allgemein vor und/oder nach dem Aufruf weitere Operationen nötig sind (z.B. Logging) → Aspect Oriented Programming (AOP) Konsequenzen: ● ● Konform mit dem Geheimnisprinzip. Proxy Typen: – – – ● Remote Proxy: Proxy für ein Remote Objekt Virtual Proxy: Proxy als Cache des realen Objekts Protection Proxy: Proxy überprüft Zugriffsberechtigung Copy-on-Write: Statt ein reales Objekt zu kopieren (was viel Zeit und/oder Speicher kosten kann) wird zunächst nur ein Proxy davon erzeugt. Erst wenn die „Kopie“ verändert werden soll, wird das reale Objekt wirklich kopiert. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 16 Proxy – Implementierung: ● Die Proxy Klasse muss nicht immer die RealSubject Klasse kennen: 1 «Interface» Subject operation() Proxy operation() – ● RealSubject operation() Ausnahme: Virtual Proxy muss eine Instanz von RealSubject erzeugt. wenn es das erste mal gebraucht wird (lazy initialization). Java Dynamic Proxy (java.lang.reflect.Proxy): Erlaubt zur Laufzeit die Erzeugung eines Proxys für ein beliebiges Objekt. Beschränkung: Dieser dynamische Proxy funktioniert nur für Methoden die in einem Interface deklariert sind. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 17 Entwurfsmuster: Command – Encapsulate a request as an object, thereby letting you parameterize clients with different requests and support undoable operations. GoF(233) – Struktur: Invoker setCommand(c:Command) Receiver action() -receiver 1 «Interface» 1 Command execute() ConcreteCommand excute() Client receiver.action() Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 18 Command – Beteiligte Klassen: ● ● ● ● ● – Command: – Definiert ein Interface für die Ausführung einer Operation. ConcreteCommand: – Implementiert execute() durch Aufruf einer korrespondierenden Aktion im gebundenen Receiver Objekt. Receiver: – Führt die eigentliche Operation aus. Invoker: – Initiiert die Ausführung der Operation via Command Interface. Client: – Bindet einen Invoker an einen Receiver via einer ConcreteCommand Instanz. Zusammenarbeit: ● ● Client erzeugt ConcreteCommand Objekt zu einem spezifischen Receiver Objekt und übergibt es der Invoker Instanz. Der Invoker ruft die execute() Operation seiner Command Instanz auf, welche den Aufruf an den Receiver weiterleitet. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 19 Command: Zusammenarbeit :Client :Invoker «create» :Receiver c:ConcreteCommand setCommand() execute() action() Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 20 Command – Anwendbar wenn ● ● ● parametrisierbare Aktion benötigt wird die Spezifizierung und Ausführung einer Aktion zu unterschiedlichen Zeiten erfolgen soll ein Undo Mechanismus benötigt wird – – Command Interface muss um eine undo() Methode erweitert werden und ConcreteCommand muss den alten Zustand speichern. Konsequenzen: ● ● Entkopplung zwischen dem Objekt, welches eine Operation einleiten will, und dem Objekt, welches weiss wie die Operation ausgeführt wird. Konform mit den Designprinzipien – – ● Geheimnisprinzip Open-Closed Prinzip Unter Einbezug des Composite Patterns können zusammen-gesetzte Kommandos aus einfachen aufgebaut werden. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 21 Entwurfsmuster: Adapter – Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. GoF(139) – – Alternativer Name: Wrapper Struktur: Client «Interface» Target request() Adapter request() -adaptee Adaptee 1 specificRequest() adaptee.specificRequest() Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 22 Adapter – Beteiligte Klassen: ● ● ● ● – Target: – Definiert ein domänenspezifisches Interface welches der Client braucht. Adaptee: – Definiert ein existierendes Interface oder Klasse, die inkompatible zum Target Interface ist. Adapter: – Klasse, die das Target Interface implementiert und alle TargetAufrufe in Adaptee-Aufrufe umsetzt. Client: – Klasse, die mit Target aber nicht mit Adaptee zusammen-arbeiten kann. Zusammenarbeit: ● ● Adapter vermittelt zwischen dem Client (der nur das Target Interface versteht) und dem Adaptee. Jeder Aufruf einer Target Methode wird vom Adapter in Adaptee Operationen umgewandelt. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 23 Adapter – Anwendbar wenn ● ● – eine Klasse wiederverwendet werden soll, deren Interface aber nicht passt Client und Target Klassen eines Frameworks sind Konsequenzen: ● ● ● Der Umfang eines Adapters hängt von der Ähnlichkeit zwischen Target Interface und Interface der wiederverwendeten Klasse (Adaptee) ab. Konform mit den Geheimnisprinzip Framework Design: Target Interfaces sollten minimalistisch entworfen werden, d.h so wenig Operationen wie möglich mit so wenig Vorraussetzungen wie möglich. Dies erleichert das Schreiben von Adapterklassen. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 24 Adapter: Ein kleines Beispiel – FileBrowser: Baumansicht des Filesystems mit Java Swing ● ● ● Client ≙ JTree Target ≙ TreeModel Adaptee ≙ File «Interface» TreeModel getRoot():Object getChildCount(parent:Object):int isLeaf(node:Object):boolean getChild(parent:Object,index:int):Object getIndexOfChild(parent:Object,child:Object):int ... File getName():String getLength():long lastModified():long isDirectory():boolean listFiles():File[] getParentFile():File ... 1 FileTreeModel -rootNode FileNode 1 getFile():File toString():String ● FileNode (auch ein Adapter) wird gebraucht, weil der Standard TreeCellRenderer einen Baumknoten mittels toString() als Text darstellt. Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 25 Adapter: FileTreeModel public class FileTreeModel implements TreeModel { private final FileNode _rootNode; public FileTreeModel(File dir) { _rootNode = new FileNode(dir); } public Object getRoot() { return _rootNode; } public int getChildCount(Object parent) { File file = ((FileNode) parent).getFile(); return file.isDirectory() ? file.listFiles().length : 0; } public boolean isLeaf(Object node) { return ((FileNode) node).getFile().isDirectory() == false; } public Object getChild(Object parent, int index) { File file = ((FileNode) parent).getFile(); return file.isDirectory() ? new FileNode(file.listFiles()[index]) : null; } public int getIndexOfChild(Object parent, Object child) { File dir = ((FileNode) parent).getFile(); File file = ((FileNode) child).getFile(); return dir.isDirectory() ? Arrays.asList(dir.listFiles()).indexOf(file) : -1; } } Universität Basel, HS 2012 © Franz-Josef Elmer 2012 Software Engineering: 11. Entwurfsmuster III 26 Adapter: FileNode und FileBrowser class FileNode { private static final NumberFormat FORMATTER = new DecimalFormat("#,##0"); private final File _file; public FileNode(File file) { _file = file; } public File getFile() { return _file; } public String toString() { String name = _file.getName(); if (_file.isDirectory() == false) { name += " [" + FORMATTER.format(_file.length()) + " bytes]"; } return name; } public class FileBrowser { } public static void main(String[] args) { File userHomeDir = new File(System.getProperty("user.home")); JTree tree = new JTree(new FileTreeModel(userHomeDir)); JFrame frame = new JFrame("FileBrowser"); frame.getContentPane().add(new JScrollPane(tree)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 500); frame.show(); } } Universität Basel, HS 2012 © Franz-Josef Elmer 2012