OBSERVER PATTERN Das Observer Pattern gehört zu den Behavioural Patterns. Es dient zur Weitergabe von Änderungen an einem Objekt an davon abhängige Strukturen. Das Pattern besteht aus einem Publisher und einem oder mehreren Subscribern (Observern). Der Publisher besitzt eine Liste von Subscribern die sein abonniert haben. Durch das attach Feature des Publishers wird ein Subscriber in die Liste eingefügt. Mit detach wird ein Subscriber entfernt. Jedes Mal wenn ein Event eintritt geht der Publisher durch seine Subscriberliste und ruft auf jedem Subscriber "update" auf.

Beispiel: Problemstellung: Auf Druck der Taste "p" soll das Programm "Demo" pausieren.

Der Publisher "pub" erkennt dass "p" gedrückt wird, daraufhin geht er durch die Subscriberliste und ruft auf jedem Element "update" auf. "Demo" erbt von Subscriber und hat sich zuvor über "subscribe" / pub.attach in die Liste des Publishers eingetragen. Daher wird demo.update aufgerufen und "Demo" kann sich selber pausieren.

Der Sinn von Subscriber.subscribe(...) ist uns nicht ganz klar, da dieses Feature nichts anderes macht als p.attach(...) aufzurufen.

VISITOR PATTERN

Problem: Assignment Attempts, um typabhängig eine bestimmte Funktion aufzurufen (Dynamic Binding).

Solution: . Trennung von Daten- und Funktionsklassen. Datenklassen erben von VISITABLE, Funktionen von VISITOR.

Visitor vs dynamic binding: Dynamic binding: + Typen hinzufügen einfach - Operationen hinzufügen schwierig - Verwandte Operationen werden getrennt (repair_binding in BOOK, repair_cover in DVD)

Visitor: - Typen hinzufügen schwierig + Operationen hinzufügen einfach + Keine assignment attempts (besser für type checking) + Verwandte Operation bleiben in einer Klasse, nicht verwandte werden getrennt

Example: Before: /maintain (b : BORROWABLE) is -- Perform maintenance on b. require exists: b /= Void local book : BOOK dvd : DVD do book ?= b if book /= Void then if book.binding_damaged then book.repair_binding end end dvd ?= b if dvd /= Void then if dvd.cover_damaged then dvd.repair_cover end end end end/ Analog für andere Operationen, z.B. display(b: BORROWABLE)...

After: BOOK und DVD erben von VISITABLE und implementieren dessen deffered Feature accept* /class BOOK inherit BORROWABLE VISITABLE feature accept (v : VISITOR) is -- Apply to v the book -- visit mechanism. do v.visit_book (Current)??? end end/ Analog für DVD

MAINTAINANCE_VISITOR erbt von VISITOR und implementiert dessen deffered Features visit_book*, visit_dvd*,... /class MAINTENANCE_VISITOR inherit VISITOR feature -- Basic operations visit_book (book: BOOK) is -- Perform maintenance operations on b. do if book.binding_damaged then book.repair_binding end end visit_dvd (dvd: DVD) is -- Perform maintenance operations on d. do if dvd.cover_damaged then dvd.repair_cover end end end /

STRATEGY PATTERN

Klassen Diagramm:

Beschreibung: Die ConcreteStrategyA und ConcreteStrategyB Klassen sind von der Selben Algorithmen Familie. Sie berechnen ähnliche Probleme. Die Klasse Strategie definiert das Problem abstrakt, also ohne konkrete Implementierung Die Klasse Context benutzt die konkreten Klassen über STRATEGY via dynamic binding als Client zur Berechnung des Algorithmus. Meist wird die konkrete Strategie von aussen (ROOT_CLASS) dem CONTEXT zugewiesen. Beispiel Schachspiel: CONTEXT: der Spieler STRATEGY : seine Spielstrategie CONCRETE_STRATEGY_A: So schnell wie möglich die Dame schnappen CONCRETE_STRATEGY_B: So gut wie möglich den König verteidigen

Vorteile Vermeidung von unübersichtlichen if-statements Erweiterbarkeit mit neuen Strategien ist einfacher (Keine Neuimplementierung von CONTEXT). Freie Wahl von verschiedenen Implementierungen

Nachteile Viel mehr Objekte, viel mehr Klassen. Viel mehr Kommunikation zwischen den Objekten. Der Benutzer (ROOT_CLASS) muss die verschiedenen Strategien kennen. Das State Pattern ist dann nützlich, wenn man ein Programm hat, welches zwischen verschiedenen Zuständen wechselt (quasi eine finite state machine) und in jedem State jeweils ähliche aber nicht gleiche Funktionalitäten bieten soll. Es wird eine deferred Klasse STATE geschrieben, wo die gewünschten features ohne Implementation definiert sind. Die effektiven States erben dann davon und werden jeweils aktiv wenn sich das Programm in diesem State befindet. Durch dieses Pattern wird der Code übersichtlicher und erweiterbar: Es gibt keine grossen select Blöcke und man kann neue States einfach direkt als neue Klasse schreiben. Ein ausführliches Beispiel zum State Pattern findet ihr im ersten Foliensatz dieses Semesters (beim letzten, "guten" Ansatz für das Flugreservationsprogramm)! Es wird dort zwar nirgends gesagt, dass es sich um das State Pattern handelt, aber genau das ist der Fall. CHAIN OF RESPONSIBILITY PATTERN Ein Sender eines Requests für die Bearbeitung eines Jobs hat mehrere Objekte in einer Kette zur Verfügung, welche diesen Job bearbeiten können. • Falls ein Objekt den Job nicht bearbeiten kann, reicht er ihn weiter an das nächste Objekt in der Kette. • Falls das Objekt den Job bearbeiten kann, wird dies gemacht und dem Sender des Requests gemeldet.

Chain of responsibility

request

PE PE PE PE PE PE

Pe: Processing element/Handler • Architektur:

next handle can_handle* APPLICATION HANDLER [G]* do_handle* handled set_next

FIRST_HANDLER [G]+ SECOND_HANDLER can_handle+ [G]+ do_handle+ can_handle+ do_handle+ handle (r: G) is do if can_handle (r) then do_handle (r); handled := True; elseif next /= Void then next.handle (r); handled := next.handled; else handled := False end end

Real World Example Firma hat Präsident, Vice-Präsident und Direktor. Chain: Direktor Æ Vice-Präsident Æ Präsident Æ VR • Falls jetzt ein Auftrag in der Höhe < 25000 Fr. kommt, bearbeitet das der Direktor. • Falls Auftrag < 50000 Fr. wird das vom Vize-Präsident entschieden. • Falls Auftrag < 100000 Fr. übernimmt das der Präsident, weil es bereits eine grosse Summe ist. • Falls Auftrag >= 100000 Fr. muss der gesamte Verwaltungsrat in einer Sitzung entscheiden, weil das eine so riesige Summe ist. Ziel Implementation von Undo/Redo Mechanismen (Ctrl Z / Ctrl Y).

Realisierung Jeder umkehrbare („undoable“) Command wird als eigene Klasse implementiert. Diese Klasse erbt von einer Master-Command-Klasse. Sie muss die deferred features implementieren/enthalten. Die History ist eine FIFO Liste welche sämtliche Commands speichert.

Masterklasse deferred class COMMAND feature done: BOOLEAN is -- Has this command been executed?

execute is -- Carry out one execution of this command. deferred ensure already: done end

undo is -- Cancel an earlier execution of this command. require already: done deferred end

redo is -- Restores an earlier execution of this command. require not_already: not done deferred end end

Ausführen eines Benutzerbefehls User Request decodieren Fall 1: Normaler Befehl – Command-Object wird erstellt und ausgeführt – in der History werden sämtliche Elemente gelöscht, welche das done-Flag nicht gesetzt haben (alle rechts der aktuellen Cursor-Position). Fall 2: Undo – Sofern an der aktuellen Cursor-Position ein Command existiert, wird dessen Undo Feature aufgerufen und die Cursor-Position um eins nach hinten verschoben. Fall 3: Redo – Sofern an der aktuellen Cursor-Position ein Command existiert, wird dessen Redo Feature aufgerufen und die Cursor-Position um eins nach vorne verschoben.

Alternative Implementation Zu jedem Command wird eine Undo- und eine Redofunktion implementiert. Anstatt in der History den Comand zu speichern wird ein Tupel mit den Agents zu Undo und Redo erstellt. Nun werden nicht mehr Execute/Redo und Undo, sondern die entsprechenden Agents aufgerufen.

ABSTRACT FACTORY PATTERN

Problem Unterschiedliche Objekte vom gleichen Typ abhängig von (evt. „äusseren“) Bedingungen nötig. Klassisches Beispiel: Verschiedenen Buttons, Fenster etc. nötig, je nach Betriebssystem.

Lösung Durch Abstract Factory: Eine Abstract Factory liefert ein Interface zur Erstellung von Objekten, ohne dass man deren dynamischen Typ kennen muss. Klassendiagramm:

Klassenbeschreibungen: Factory: Deferred Class. Stellt ein generelles Interface für die Erstellung von Produkten dar. Listet die einzelnen Produkte als deferred Features: new_produkt_a (args): PRODUCT_A is deferred end new_produkt_b: PRODUCT_B is deferred end … PRODUCT_A und PRODUCT_B sind selbst ebenfalls deferred. Factory_1 / Factory_2: Erben von Factory und liefern unterschiedliche Implementationen der Instanzierung der einzelnen Produkte. new_produkt_a (args): PRODUCT_A is do create {PRODUCT_A1} Result.make (args) end oder: new_produkt_a (args): PRODUCT_A1 is do create Result.make (args) end

Factory Objekt erstellen, Variante 1: feature -- Access factory: FACTORY feature --Something initialize is -- factory initialization do if some_condition then create {FACTORY_1} factory else create {FACTORY_2} factory end end

Factory Objekt erstellen, Variante 2 mit Singleton (empfohlen) feature – Singleton factory: FACTORY is -- factory singleton once if some_condition then create {FACTORY_1} Result else create {FACTORY_2} Result end end Factory call my_product := factory.new_product_a

SINGLETON PATTERN (Creational) Source: Gamma et al. (1994); also known as „Gang of Four“ (GoF); pages 127ff:

Intent „Ensure a class only has one instance, and [to] provide a global point of access to it.“

Motivation and Applications Printer spooler, window manager, accounting system, calendar application, ... all applications that include some components that should only be created once, but will be used throughout the whole running of the application. An example might be a music-play-tool for playing mp3 (e.g.), where you create the main player-object only once and where exists only one instance of the player-object - which clearly makes sense. There are many other examples of which all ensure that an instance is only created once and that all other components of the application have access to it. A global variable makes an object accessible, but it does not keep you from creating multiple instances. The class itself has to ensure that only one instance of itself is created.

Consequences 1. Controlled access to sole instance. Singleton class encapsulates its sole instance and thereby controls how and when clients can access it. 2. Reduced name space. avoids pollution of name space by global variables. 3. Permits refinement of operations and representation. Singleton class may be subclassed, i.e., different versions of singleton classes may be possible. Which version is needed can be decided at run-time through system configuration 4. Permits variable number of instances (not just one) 5. More flexible than class operations. Singleton's functionality might also be achieved by class operations (static member functions in C++). But those cannot be virtual and subclasses cannot override them polymorphically.

Implementation Implementation issues to be considered when using Singleton patterns: 1. Ensuring a unique instance. This is achieved by hiding the operation creating the instance behind a class operation. In C++ this would be a static member function of the class. Only this operation has access to the variable holding the sole instance. Furthermore this operation makes sure that only one instance is created. 2. Subclassing the Singleton class. In different runs of the system, we might want to have different versions of the singleton. Each version is implemented in a subclass. Which version to use is decided at run-time via some configuration mechanism. A flexible approach for subclassing is to use a registry of singletons. This concluded the summary of the singleton pattern. In what follows below, problems and implementation issues of the singleton pattern in Eiffel are summarized. The material is taken from: Karine Arnout (2004); Diss 15550 ETHZ; chapter 18 pages 289ff (http://se.inf.ethz.ch/people/arnout/patterns/download/karine_arnout_phd_thesi s.pdf) and from Bertrand Meyer's slides.

Problem with Singleton in current Eiffel Class ANY has features clone and deep_clone => any Eiffel object can be duplicated which rules out the Singleton pattern

Best Implementation of Singleton with current Eiffel No static features in Eiffel (as in C++) => use once routines.

1. The and Contracts approach Make a class inherit from class SINGLETON to specify that it can only have one instance thanks to Singleton's invariant. Class SINGLETON has a frozen feature the_singleton which returns the unique instance of the class. Provide a global access point to the unique instance through a class SHARED_SINGLETON. This implementation does not work, because if one inherits from class SINGLETON several times, feature the_singleton, because it is a once function inherited by all descendants would keep the value of the first created instance. Then all descendants would share the same value. This violates the invariant of SINGLETON in all descendant classes except the one for which the singleton was created first.

2. Singleton with creation control The previous implementation is corrected by adding a boolean feature may_create_singleton in the accessor class MY_SHARED_SINGLETON. In the corresponding MY_SINGLETON class, the creation procedure includes may_create_singleton in its precondition. Unfortunately, this still does not solve the correctness problem. Yet another example of singletons in Eiffel is in the Gobo library.

Proposed Implementation in a future version of Eiffel Use frozen classes where a frozen class cannot have any descendants. Singleton pattern can be implemented using two classes: A frozen class SHARED_SINGLETON exposing a feature singleton which is a once function returning an instance of type SINGLETON. A class SINGLETON whose creation procedure is exported to class SHARED_SINGLETON and its descendants only. Problem: Frozen classes violate the open – closed principle, because they cannot have any descendants. For further information see the Slides or the Phd-Thesis from Karine Arnout.