Observer-Pattern (Java vs. .NET vs. Qt) Seminar Software-Entwurf Tim Rademacher Gliederung • Einführung in Design Patterns • Das Observer-Pattern – Ein Beispiel • Das Observer-Pattern in Java – Das Interface Observer – Die Klasse Observable • Events beim Java AWT • Delegates und Events in .NET • Signale und Slots in Qt Tim Rademacher: Observer-Pattern 2 Einführung in Design Patterns Design Pattern sind Beschreibungen von Lösungen für Software-Design Probleme Design Pattern müssen • den Kontext, • das Problem • und die Lösung darstellen. Beschreibungen der Pattern in festgelegter Form • Vergleichbarkeit • Kategorisierbarkeit Tim Rademacher: Observer-Pattern 3 Einführung in Design Patterns Nutzen von Design Patterns • Ein gemeinsames Design Vokabular – einfache Möglichkeit zu Kommunikation und Dokumentation • Eine Dokumentations- und Lernhilfe – schnelles und leichtes Verständnis von Beschreibungen – Design Patterns für allgemeine oft vorkommende Probleme • Eine Erweiterung zu existierenden Methoden – Design Patterns beinhalten Erfahrung von Design Experten • Ein Ziel für Refactoring – inflexible Software umorganisiert Tim Rademacher: Observer-Pattern 4 Das Observer-Pattern – Ein Beispiel Unsere aktuelle Situation: Ein aktiver Erzähler V ● ● Vortragender Beobachteter / ist beobachtbar (engl. Observable) macht keine Unterscheidung zwischen den Zuhörern ● hört auf, wenn keiner zuhört Anzahl passiver Zuhörer ● ● Zuhörer 1 Zuhörer 2 Zuhörer 3 Zuhörer x ● Beobachter (engl. Observer) interessiert an Informationen des Erzählers reagieren auf diese Informationen Tim Rademacher: Observer-Pattern 5 Das Observer-Pattern in Java Das Interface Observer ● ● wird von Beobachtern implementiert enthält nur eine MethodenDeklaration «interface» Observer + update(Observable, Object) : void Tim Rademacher: Observer-Pattern 6 Das Observer-Pattern in Java Die Klasse Observable Observable ● ● - changed: boolean = false obs: Vector + + + + + + # # + + Observable() addObserver(Observer) : void deleteObserver(Observer) : void notifyObservers() : void notifyObservers(Object) : void deleteObservers() : void setChanged() : void clearChanged() : void hasChanged() : boolean countObservers() : int ● ● ● Superklasse von Beobachteten besitzt Methoden zum hinzufügen, entfernen von Beobachtern speichert seine Beobachter in dem Vector obs Die notifyObservers-Methoden rufen die update-Methode der Beobachter auf teilt Änderung des Objektzustands nach außen hin mit Tim Rademacher: Observer-Pattern 7 Das Observer-Pattern in Java Übersicht und Anmerkung: Observable «interface» Observer - changed: boolean = false obs: Vector + + + + + + # # + + Observable() addObserver(Observer) : void deleteObserver(Observer) : void notifyObservers() : void notifyObservers(Object) : void deleteObservers() : void setChanged() : void clearChanged() : void hasChanged() : boolean countObservers() : int + update(Observable, Object) : void Normalerweise enden die Namen von Interfaces immer auf "able", hier jedoch umgekehrt! Tim Rademacher: Observer-Pattern 8 Das Observer-Pattern in Java Zurück zu unserem Beispiel, Erzähler: Observable - changed: boolean = false obs: Vector + + + + + + # # + + Observable() addObserver(Observer) : void deleteObserver(Observer) : void notifyObservers() : void notifyObservers(Object) : void deleteObservers() : void setChanged() : void clearChanged() : void hasChanged() : boolean countObservers() : int public class Erzaehler extends java.util.Observable { public void erzaehle(String information){ if(countObservers()>0){ setChanged(); notifyObservers(information); } } } Erzaehler + erzaehle(String) : void Tim Rademacher: Observer-Pattern 9 Das Observer-Pattern in Java Zu unserem Beispiel, Zuhörer: public class Zuhoerer implements java.util.Observer { «interface» public Zuhoerer() { } Observer + update(Observable, Object) : void public void update(java.util.Observable o, Object information) { if(isNeuigkeit(information)){ merken(information); } } Zuhoerer + + - private void merken(Object information) { //... } Zuhoerer() update(Observable, Object) : void merken(Object) : void isNeuigkeit(Object) : boolean private boolean isNeuigkeit(Object information) { //... } } Tim Rademacher: Observer-Pattern 10 Events beim Java AWT • Benutzer interagiert mit Komponenten der grafischen Oberfläche • Events/Ereignisse werden gesendet • Die Applikation reagiert auf die Events • Ereignis-Klassen sind kategorisiert • alle Events der grafischen Oberfläche sind eine Unterklasse der abstrakten Klasse AWTEvent Tim Rademacher: Observer-Pattern 11 Events beim Java AWT java.awt.AWTEvent java.io.Serializable java.util.EventObject ~ serialVersionUID: long = 5516075349620653480L source: Object + + + EventObject(Object) getSource() : Object toString() : String # # ~ ~ + + + + + + + + + + + + ~ + + + + + + + + - bdata[]: byte id: int consumed: boolean = false focusManagerIsDispatching: boolean = false isPosted: boolean COMPONENT_EVENT_MASK: long = 0x01 CONTAINER_EVENT_MASK: long = 0x02 FOCUS_EVENT_MASK: long = 0x04 KEY_EVENT_MASK: long = 0x08 MOUSE_EVENT_MASK: long = 0x10 MOUSE_MOTION_EVENT_MASK: long = 0x20 WINDOW_EVENT_MASK: long = 0x40 ACTION_EVENT_MASK: long = 0x80 ADJUSTMENT_EVENT_MASK: long = 0x100 ITEM_EVENT_MASK: long = 0x200 TEXT_EVENT_MASK: long = 0x400 INPUT_METHOD_EVENT_MASK: long = 0x800 INPUT_METHODS_ENABLED_MASK: long = 0x1000 PAINT_EVENT_MASK: long = 0x2000 INVOCATION_EVENT_MASK: long = 0x4000 HIERARCHY_EVENT_MASK: long = 0x8000 HIERARCHY_BOUNDS_EVENT_MASK: long = 0x10000 MOUSE_WHEEL_EVENT_MASK: long = 0x20000 WINDOW_STATE_EVENT_MASK: long = 0x40000 WINDOW_FOCUS_EVENT_MASK: long = 0x80000 RESERVED_ID_MAX: int = 1999 serialVersionUID: long + + + + + + # # ~ ~ initIDs() : void AWTEvent(Event) AWTEvent(Object, int) setSource(Object) : void nativeSetSource(ComponentPeer) : void getID() : int toString() : String paramString() : String consume() : void isConsumed() : boolean convertToOld() : Event copyPrivateDataInto(AWTEvent) : void Tim Rademacher: Observer-Pattern java.awt.event.ActionEvent + + + + + + + ~ ~ ~ - SHIFT_MASK: int = Event.SHIFT_MASK CTRL_MASK: int = Event.CTRL_MASK META_MASK: int = Event.META_MASK ALT_MASK: int = Event.ALT_MASK ACTION_FIRST: int = 1001 ACTION_LAST: int = 1001 ACTION_PERFORMED: int = ACTION_FIRST actionCommand: String when: long modifiers: int serialVersionUID: long + + + + + + + ActionEvent(Object, int, String) ActionEvent(Object, int, String, int) ActionEvent(Object, int, String, long, int) getActionCommand() : String getWhen() : long getModifiers() : int paramString() : String 12 Events beim Java AWT InputEvent • Zusammenfassung von ähnlichen Ereignissen zu Gruppen • Anzahl der Event-Klassen wird klein gehalten • Unterscheidung von Events innerhalb einer Eventklasse durch IDs • Abfrage der ID über Methode AWTEvent.getID() MouseEvent + + + + + + + + + + + + + + ~ ~ ~ ~ ~ - MOUSE_FIRST: int = 500 MOUSE_LAST: int = 507 MOUSE_CLICKED: int = MOUSE_FIRST MOUSE_PRESSED: int = 1 + MOUSE_FIRST MOUSE_RELEASED: int = 2 + MOUSE_FIRST MOUSE_MOVED: int = 3 + MOUSE_FIRST MOUSE_ENTERED: int = 4 + MOUSE_FIRST MOUSE_EXITED: int = 5 + MOUSE_FIRST MOUSE_DRAGGED: int = 6 + MOUSE_FIRST MOUSE_WHEEL: int = 7 + MOUSE_FIRST NOBUTTON: int = 0 BUTTON1: int = 1 BUTTON2: int = 2 BUTTON3: int = 3 x: int y: int clickCount: int button: int popupTrigger: boolean = false serialVersionUID: long + + + + + + + + + + + - initIDs() : void MouseEvent(Component, int, long, int, int, int, int, boolean, int) MouseEvent(Component, int, long, int, int, int, int, boolean) getX() : int getY() : int getPoint() : Point translatePoint(int, int) : void getClickCount() : int getButton() : int isPopupTrigger() : boolean getMouseModifiersText(int) : String paramString() : String setNew Modifiers() : void setOldModifiers() : void readObject(ObjectInputStream) : void Tim Rademacher: Observer-Pattern 13 Events beim Java AWT • Ereignisquellen (z.B. JButton) können Events aussenden • EventListener sind an den Events interessiert • EventListener meldet sich bei der EventSource an • Bei auftretenden Events Informiert die EventSource alle registrierten EventListener • Es gibt für jedes Event eine eigene Listener Klasse «interface» java.util.EventListener «interface» java.awt.event.ActionListener + actionPerformed(ActionEvent) : void Tim Rademacher: Observer-Pattern 14 Das Observer-Pattern in .NET .NET bietet eigene Programmkonstrukte zum Benutzen des Observer-Pattern: • Delegates und • Events • Delegates übermitteln Funktionszeiger von Ereignis-Konsumenten zu -Produzenten • Schnittstelle des Ereignisproduzenten in Form von Ereignissen Tim Rademacher: Observer-Pattern 15 Das Observer-Pattern in .NET //Deklaration eines Delegate-Typs public delegate void ChangeEvent(object src, string s); //Deklaration einer Delegate-Variablen public event ChangeEvent onChangeEvent; //Zuweisung einer passenden Methode an eine Delegate-Variable onChangeEvent = new ChangeEvent(Hoere); public void Hoere(object src, string information) { ... } //Aufruf von Hoere(einObj, "string"); onChangeEvent(einObj, "string"); • Delegate-Variable kann mehrere Delegate-Objekte aufnehmen onChangeEvent += new ChangeEvent(OnChange0); onChangeEvent += new ChangeEvent(OnChange1); onChangeEvent -= new ChangeEvent(OnChange1); Tim Rademacher: Observer-Pattern 16 Das Observer-Pattern in .NET • Delegate-Felder als event deklariert, da – Bessere Kapselung, nur die Klasse, die das Event deklariert, kann es auslösen – Zugriff von außen nur mit += oder -= • Anmerkung, Konvention für Event-Handling Delegates: – Kein Rückgabewert (void) – 1. Parameter, Sender des Events, Typ object – 2. Parameter, Event-Parameter, Subklasse von System.EventArgs (wird im Beispiel nicht beachtet) Tim Rademacher: Observer-Pattern 17 Das Observer-Pattern in .NET public class Erzaehler { //Delegate-Typ public delegate void ChangeEvent(object src, string s); //Delegate-Variablen public event ChangeEvent onChangeEvent; public void Erzaehle(string information){ if (onChangeEvent != null) onChangeEvent(this, information); } ... Vortragender } public class Zuhoerer { public Zuhoerer { ... //Anmeldung beim Erzaehler instanzErzaehler.onChangeEvent += new ChangeEvent(Hoere); ... } Zuhörer 1 Zuhörer 2 Zuhörer 3 Zuhörer x public void Hoere(object src, string information) { if(IsNeuigkeit(information)){ Merken(information); } } ... } Tim Rademacher: Observer-Pattern 18 Das Observer-Pattern in Qt • Qt ist eine C++ Klassenbibliothek und ein GUI-Toolkit für Unixund X11-Systeme • Signale und Slots – Schlüsselwort signals: zur Deklaration der Signale, Signale müssen nicht Implementiert werden – Schlüsselwort slots: zur Deklaration der Slots – aussenden von Signalen mit emit, z.B. emit highlighted(5); – Verbinden von Signal mit einem Slot mit Hilfe der Methode Qobject::connect • zusätzlicher Preprozessor, 'Meta Object Compiler' (kurz moc) extrahiert Informationen über Signale und Slots, erzeugt Verbindungscode Tim Rademacher: Observer-Pattern 19 Das Observer-Pattern in Qt Beispiel: //Erzähler //Zuhörer signals: erzaehleInformation(string s); ... slots: empfangeInformation(string s); ... public void erzaehle(string information){ emit erzaehleInformation(information); } public void empfangeInformation(string s){ if(isNeuigkeit(s)){ merken(s); } } Qobject::connect(erzaehler, SIGNAL(erzaehleInformation(string)), zuhoerer, SLOT(empfangeInformation(string))); Tim Rademacher: Observer-Pattern 20 Events in Qt vs. Java vs. .NET Java Implementierung EventListener und Events Codelesbarkeit +, da vertraut Flexibilität (-), wegen Hierachie .NET Qt Delegates und Signale und Events Slots -, da nicht vertraut +, da simpel +, nur Konventionen+, nur Parameter Einhalten Beachten Tim Rademacher: Observer-Pattern 21 Das Observer-Pattern Danke für die Aufmerksamkeit! Tim Rademacher: Observer-Pattern 22