790 likes | 1.15k Views
Programmering og systemudvikling. Lektion 8 Designmønstre. Observer – en starter. Ur. Clock Hour: 11 Min: 38 Sek: 25 MSek:. is presented as. depends on. depends on. Regneark. Celler D7: 25 D8: 10 D9: 65. is presented as. depends on. depends on. Lagkage- diagram.
E N D
Programmering og systemudvikling Lektion 8 Designmønstre Designmønstre
Observer – en starter Designmønstre
Ur Clock Hour: 11 Min: 38 Sek: 25 MSek: .. is presented as depends on depends on Designmønstre
Regneark Celler D7: 25 D8: 10 D9: 65 is presented as depends on depends on Lagkage- diagram Designmønstre
CAD-system Browser ... House DoorA Roof1 Window3 is presented as depends on depends on Designmønstre
CASE-værktøj Browser ... AST is presented as depends on depends on class Account { public void deposit(...); public void withdraw(...); }; class ChequeAccount extends Account { ... }; ... Designmønstre
Generelt: Subject-Observers depends on Subject is presented as Observer 1 Observer 2 ... Observer n Designmønstre
Problem • Ændring af den interne tilstand i en komponent kan bevirke inkonsistens i andre eller på tværs af komponenter. • Hvordan kan vi reetablere konsistens således at: • informationsudbyderen (subject) ikke afhænger af forbrugerne (observers) • forbrugerne (observers) ikke skal være kendt på forhånd. Designmønstre
Løsning • Implementer en “change propagation mechanism” mellem informationsudbyder (Subject) og forbrugere (Observers). • Subject vedligeholder et register over Observers og gør alle Observers opmærksomme på ændringer af tilstand-en. • Observer erklærer en (virtuel) update-funktion som kaldes af Subjects “change propagation mechanism”. • Konkrete Observers implementerer update-metoden... Designmønstre
abstract (videre-)binding Løsning, struktur Designmønstre
attach(this) attach(this) Løsning, dynamik Subject Observer 1 Observer 2 setData notify update getData update getData Designmønstre
Om mønstre Abstraktioner og sprogmekanismer Gang of Four (GoF) En skabelon for mønstre (eks. Observer) Designmønstre
Abstraktioner og sprogmekanismer Sprogmekanismer Tid ... Arkitektoniske abstraktioner Designmønstre
Eksempler goto call return record array class object Tid Kræver stor disciplin og systematik af udvikleren. Ingen sprog- understøttelse. goto sr a:... s:... goto a Simulering af abstrakte datatyper (ADT) ? Manuel allokering og administration af lagerblokke; manuel adresse- beregning ved indeksering, ... Designmønstre
... Eksempler goto call return record array class object Tid goto sr a:... s:... goto a Simulering af abstrakte datatyper (ADT) Mønstre Manuel allokering og administration af lagerblokke; manuel adresse- beregning ved indeksering, ... Designmønstre
Gang of Four (GoF) Erich Gamma, Richard Helm Ralph Johnson & John Vlissides Design Patterns – Elements of Reusable Object-Oriented Software Addison-Wesley, 1995. (Også udgivet som CD, 1998) Første systematiske fremstilling af designmønstre. Designmønstre
Designmønstre Et designmønster - navngiver, - abstraherer og - identificerer de centrale aspekter ved en gængs designstruktur. Et designmønster identificerer deltagende klasser (og instanser) deres rolle og samarbejde samt ansvarsfordelingen mellem dem. The Pattern Community: Aggresive disregard of originality Designmønstre
Designmønstre i GoF (23) Creational (5) Structural (7) Behavioral (11) Abstract Factory Adapter Chain of Responsibility Builder Bridge Command Factory MethodComposite Interpreter Prototype Decorator Iterator Singleton Facade Mediator Flyweight Memento Proxy Observer State Strategy Template Method Visitor Designmønstre
Designmønstre á la PiJ 1. Introduction 2. A Case Study 3. Fundemental Patterns (4) 4. Creational Patterns (5) 5. Partitioning Patterns (6) 6. Structural Patterns (7) 7. Behavioral Parrerns (8) 8. Concurrency Patterns (9) 6. Conclusion - synopsis - context - forces - solution - consequences - implementation - Java API usage - code example - related patterns Designmønstre
Observer Intent Definerer en en-til-mange sammenhæng mellem objekter så ændringer af tilstanden i et objekt automatisk reflekteres i alle de andre objekter (Publish-Subscribe). Motivation Designmønstre
Observer (2) Structure Designmønstre
Observer (3) Participants Subject, kender sine (abstrakte) observere; et vilkårligt antal observere kan knyttes til et subject; giver gennem sit interface mulighed for at tilknytte og fjerne observere Observer, definerer et interface til at opdatere objekter der skal bekendtgøres om ændringer i et subject ConcreteSubject, indkapsler tilstand for konkrete subjects; bekendtgør tilstandsændringer til observere ConcreteObserver, vedligeholder en reference til et ConcreteSubject objekt; indkapsler tilstand der skal være konsistent med subject Designmønstre
Observer (4) Collaboration Designmønstre
Observer, Sample Code (1) interface Observer { public abstract void update(Subject theChangedSubject); }; abstract class Subject { public void attach(Observer o){observers.add(o);} public void Detach(Observer o) {observers.remove(o);} public void notify() {…}; protected Subject(); private List observers; }; Designmønstre
Observer, Sample Code (2) public void notify() { iterator i = observers.iterator(); while (i.hasNext()) (Observer)(i.next()).update(this); } Designmønstre
Observer, Sample Code (3) class ClockTimer extends Subject { public ClockTimer(); public int getHour(); public int getMinute(); public int getSecond(); void tick(); // Tick kaldes jævnligt af en intern timer }; void ClockTimer::tick() { // opdatér intern repræsentation // ... notify(); } Designmønstre
Observer, Sample Code (4) class DigitalClock implements Widget extends Observer { public DigitalClock(ClockTimer); public void update(Subject) // (re-)definerer Observer::update public void draw(); // (re-)definerer Widget::draw private ClockTimer _subject; }; Designmønstre
Observer, Sample Code (5) DigitalClock::DigitalClock(ClockTimer s) { _subject = s; _subject.attach(this); } DigitalClock::finalize() { _subject.detach(this); } void DigitalClock::Update(Subject ChangedSubject) { if (ChangedSubject == _subject) draw(); } void DigitalClock::Draw() { // skaf de nye værdier fra _subject int hour = _subject.getHour(); int minute = _subject.getMinute(); // etc. // tegn det digitale ur } Designmønstre
Iterator Intent Giver sekventiel tilgang til elementerne i en container (et aggregat) uden at afsløre containerens underliggende repræsentation. Motivation Problemstillingen og et bud på en løsning er velkendt: Designmønstre
Iterator (2) Motivation, fortsat Imidlertid er det ufleksibelt at en klient skal vide at det er en liste der itereres på, men det kan undgås: Designmønstre
Iterator (3) Structure CreateIterator er et eksempel på en Factory Method (GoF, pp. 107-116). Designmønstre
Composite Intent Sammensæt objekter i en rekursiv træstruktur der repræsenterer et part-whole hierarki. Mønsteret muliggør at enkelte og sammensatte elementer kan behandles uniformt. Motivation Designmønstre
Composite (2) Structure Designmønstre
Composite (3) Participants Component (Graphic) - erklærer et interface for objekter i strukturen - implementerer standardopførsel for alle objekter - erklærer et interface til “child“-komponenter (evt.) - erklærer et interface til “parent”-komponenter. Leaf (Rectangle, Line, Text, etc.) - repræsenterer primitive objekter (blade) - (re-)definerer opførsel for primitive objekter. ... Designmønstre
Composite (4) Participants, fortsat Composite (Picture) - (re-)definerer opførsel for sammensatte objekter - gemmer referencer til efterfølgerobjekter - implementerer efterfølger-relaterede operationer fra interfacet. Client - manipulerer Component-objekter gennem Component- interfacet. Designmønstre
Composite (5) Implementation ... Skal operationer til håndtering af sammensatte objekter erklæres i Component eller i Composite? Svaret forudsætter en afvejning mellem sikkerhed og transparens; i dette designmønster er transparens fundet vigtigere end sikkerhed, men bemærk at det er i konflikt med substitutionsprincippet. Hvis operationerne flyttes ned i Composite, opstår der i Client behov for på runtime at kunne spørge på typen af et Component-objekt. I C++ kan man bruge dynamic_cast eller tilføje en operation getComposite i Component-interfacet: Designmønstre
Composite, sikkerhed over transparens class Component { public: // ... Composite getComposite() { return null; } }; class Composite extends Component { public: add(Component); remove(Component); // ... Composite getComposite() { return this; } }; class Leaf extends Component { // ... } Designmønstre
Template Method, GoF pp. 325-330 Intent At definere grundstrukturen i en algoritme, men udskyde den konkrete fastlæggelse af enkelte trin til subklasser. Subklasser kan redefinere elementer i en algoritme, men grundstrukturen ligger fast. Motivation Designmønstre
Template Method (2) Class Application { public void OpenDocument(String name) { if ( !CanOpenDocument(name) ) { // cannot handle this document return; } Document doc = DoCreateDocument(); if ( doc ) { _docs.AddDocument(doc); AboutToOpenDocument(doc); doc.Open(); doc.DoRead(); } private Vector _docs; } Designmønstre
Template Method (3) Operationen ’move’ i et figurhierarki er en Template Method: class Shape { public Shape(int x, int y) _x(x), _y(y) {} public void move(int dx, int dy) { hide(); _x+= dx; _y+= dy; show(); } public abstract void show(); public abstract void hide(); protected int _x; protected int _y; }; Designmønstre
Template Method (4) Structure Designmønstre
Adapter, GoF pp. 139-150 Intent Konverterer interfacet for en klasse til et som klinten forventer (wrapper, late abstraction). Motivation Det kan ske at en klasse er svær at genbruge fordi den har et “forkert” interface (eks.: Vector og List burde kunne behandles uniform, men har forskellige interfaces). Eller: Til et tegneprogram kan det være ligetil at implementere klasserne Shape, LineShape, PolygonShape, hvorimod TextShape er noget mere kompliceret; selv basal teksteditering involverer komplicerede skærm-opdateringer og buffer-håndtering. Designmønstre
Adapter, Motivation fortsat Imidlertid findes der en ‘off-the-shelf’ klasse, TextView; problemet er blot at denne ikke er designet – og derfor ikke kan behandles – som en Shape. Hvis vi har kildeteksten til TextView kan vi ændre interfacet så det passer til Shape, men ofte har man ikke kildeteksten, og selv om man har, er det af hensyn til andre applikationer ikke ønskeligt at tilpasse interfacet. Men vi kan definere TextShape som en adapter der tilpasser TextViews interface til Shape-hierarkiet. Dette kan gøres på to måder: Class Adapter Object Adapter Designmønstre
Adapter public class Shape { // BoundingBox-based public Shape(){…} public Box BoundingBox(); public Manipulator CreateManipulator(); }; class TextView { // OriginAndExtent-based public: TextView(); //returnerer centrum public point GetOrigin(Coord& x, Coord& y){…} //retrunerer bredde og højde public pair GetExtent(){…} public bool isEmpty(){…} }; Designmønstre
Class Adapter (C++ style) class TextShape : public Shape, private TextView { public: TextShape(); virtual void BoundingBox(Point& bottomLeft, Point& topRight); virtual bool isEmpty(); virtual Manipulator* CreateManipulator(); }; void TextShape::BoundingBox(Point& bottomLeft, Point& topRight) { Coord bottom, left, width, height; GetOrigin(bottom, left); GetExtent(width, height); bottomLeft = Point(bottom, left); topRight = Point(bottom + height, left + width); } Designmønstre
Adapter for containerklasser template<class T> class Vector { public: explicit Vector(size_t n); T& operator[](size_t); }; template<class T> class List { public: class Link { ... }; List(); void put(T*); T* get(); }; Hver klasse tilbyder ”de naturlige operationer” – de er små og kan inlines (effektivt) – og for hver klasse kan vi vælge en passende repræsentationsinvariant uden at tænke på de øvrige containere. Imidlertid kan forekomster af de to forskellige slags containerklassser ikke behandles uniformt (for eksem- pel med en iterator), og det er træls! Hvad gør vi? Designmønstre
abstract wrap wrap Wrap, wrap, and abstract Late Abstraction Designmønstre
Koden... template<class T> class Itor { public: virtual T* first()= 0; virtual T* next()= 0; }; template<class T> class VectorItor : public Itor { public: VectorItor(Vector<T>& vv) : v(vv), index(0) {} T* first() { ... } T* next() { ... } private: Vector<T>& v; size_t index; }; template<class T> class ListItor : public Itor { public: ListItor(List<T>& llst) : lst(llst), ... {} T* first() { ... } T* next() { ... } private: List<T>& lst; List<T>::Link p; }; Designmønstre
Anvendelser int count (Itor<int>& i, int x) { int c = 0; for ( int* p=i.first(); p; p=i.next() ) if ( *p==x ) c++; return c; } Vector<int> v; VectorItor<int> vi(v); int periodCount = count(vi, 7); List<int> l; ListItor<int> li(v); int commaCount = count(li, 42); Operationerne first() og next() er simple, men de giver et overhead i form af et virtuelt funktionskald. Til et standardbibliotek er det ikke ideelt, men løsningen har dog været brugt i et utal af systemer, og i mange år var det faktisk Bjarne Stroustrups favoritløsning. Lige indtil Alexander Stepanov og Meng Lee fra HP kom med STL... Designmønstre
Hvad vi lærte af “Late Abstraction” Bemærk: Et fælles interface som Itor kan laves længe efter at containerklasserne er designet og implementeret. Når vi designer og program-merer, udvikler vi typisk først noget meget konkret (for ek-sempel Vector og List). Først senere erkender vi ab-straktioner der kan generali-sere de mere konkrete kom-ponenter (f. eks. Itor). ”Late abstraction” kan vi benytte gentagne gange. ”Late abstraction” vha. abstrakte klasser tillader os at lave forskellige implementa-tioner af et begreb også når der ikke er en åbenlys lighed mellem implementationerne. Vector og List har oplagte fællestræk, men der er ikke noget i vejen for også at lave en Itor for eksempelvis en stream. Designmønstre