Ein Design Pattern beschreibt ein Problem, das immer wieder in unserer Umgebung anzutreffen ist. Dabei soll die Lösung für dieses Problem so sein, dass Sie diese Lösung millionenfach verwenden können, ohne diese zweimal auf dieselbe Weise zu verwenden.
Christopher Alexander
Design Pattern sind Beweis für bewährte Software in der Praxis. Ein Baustein mit verschiedenen Abstraktionsebenen: Idiom (Coplien, 1991), Design Pattern (Gamma et al., 1995) und Architekturmuster (Buschmann et al., 1996).
Idiome
Idiome sind keine objektorientierte Design Patterns!
Ein Idiom ist ein Muster auf niedriger Ebene, normalerweise auch spezifisch für eine Programmiersprache. Hier zum Beispiel String-Copy in C, wobei s und d Zeichenarrays sind.
while (* d ++ = * s ++);
Faule Instantiierung von Singletons in Java (Double-checked Locking Idiom)
private static Device device = null;
public static Device instance() {
if (device == null) {
synchronized (Device.class) {
if (device == null) {
device = new Device();
}
}
}
return device;
}
Template Method
Designziel ist beispielsweise einen Algorithmus so zu implementieren, dass bestimmte Abschnitte später angepasst oder geändert werden können. Definieren Sie hierzu ein Skelett des Algorithmus in einer Operation und lagern Sie dabei einige Schritte auf Unterklassen aus.
Diese Herangehensweise wir oft in Frameworks und APIsverwendet.
Verwenden Sie das Template Method Pattern, für getrennte Varianten und Invarianten. Vermeiden Sie dadurch Duplizierung von Code. Das allgemeine Verhalten wird in einer gemeinsamen Klasse zusammengefasst und dirt lokalisiert.
Die Template Method ist die Methode, die den Algorithmus unter Verwendung abstrakter und konkreter Operationen definiert. Außerdem können abstrakte Operationen überschrieben und Hook-Operationen definiert werden.
Motivation hinter Design Pattern
Da das entwickeln von wiederverwendbarer und erweiterbarer Software oft sehr schwer ist und Entwickler, welche zuvor nicht bei der Entwicklung dabei waren meistens überfordert sind, hat man Pattern eingeführt um sich leichter einen Überblick verschaffen zu können. Pattern sind stark an Erfahrungen angelehnt und einige Designlösungen kommen immer wieder vor. Dadurch ist das Verständnis wiederkehrender Lösungen leichter zu verstehen und gibt viel Information wo welche Änderungen vorgenommen werden müssen. Dadurch hat dich der Aufbau auf eine generische Weise etabliert durch die Konsequenzen und Kompromisse leichter verständlich sind.
Architektonische Muster oder Architectural Patterns
Architektonische Muster sind keine Design Pattern!
Architekturmuster helfen lediglich, die grundlegende Struktur eines Softwaresystems oder wichtiger Teile davon zu bestimmen. Architekturmuster haben einen wesentlichen Einfluss auf das Erscheinungsbild konkreter Softwarearchitekturen.
Dabei definieren Architekturmuster die globalen Eigenschaften eines Systems, zum Beispiel, wie verteilte Komponenten zusammenarbeiten und Daten austauscht werden oder auch die Grenzen für Subsysteme. Die Auswahl eines Architekturmusters ist eine grundlegende Entwurfsentscheidung.
Bekannte Architekturmuster sind dabei:
- Pipes and Filters
- Broker Pattern
- MVC
- Broker
Oft reicht es aber nicht aus, nur ein architektonisches Muster zu wählen. Stattdessen müssen mehrere architektonische Muster kombiniert werden.
Model-View Controller (MVC)
Das MVC-Muster beschreibt eine grundlegende strukturelle Organisation für interaktive Softwaresysteme dabei enthält das Modell die wichtigsten Funktionen und Daten wobei es unabhängig von Ausgabedarstellungen oder Eingabeverhalten ist.
Die Benutzeroberfläche besteht aus:
• der Ansicht / Ansichten, die dem Benutzer Informationen anzeigen. Die Ansicht bezieht die Daten aus dem Modell.
• dem Controller, der die Benutzereingaben verarbeitet.
Dabei hat jede Ansicht einen eigenen Controller, der die Eingaben empfängt. Die Ereignisse werden dann in Serviceanforderungen für das Modell oder die Ansicht übersetzt. Alle Interaktionen laufen dabei über einen Controller.
Model-View Controller (MVC) Change Propagation
Die Change Propagation stellt die Konsistenz zwischen der Benutzeroberfläche und dem Modell sicher. Dieser Mechanismus wird normalerweise mithilfe des Observer-Musters (Observer pattern) und dem Publisher-Subscriber-Musters (Publisher Subscriber pattern) implementiert. Dabei ist die Grundidee, eine Ansicht „registriert“ sich am Modell. Wenn das Verhalten eines Controllers vom Status des Modells abhängt, „registriert“ sich der Controller beim Mechanismus für die Weitergabe von Änderungen.
Verwenden Sie das MVC-Muster zum Erstellen interaktiver Anwendungen mit einer flexiblen „Mensch-Computer“-Schnittstelle. Wenn die gleichen Informationen unterschiedlich dargestellt werden sollen, zum Beispiel in verschiedenen Fenstern. Oder die Anzeige und das Verhalten einer Anwendung die Veränderungen von Daten sofort widerspiegeln müssen. Oder zum Portieren der Benutzeroberfläche da dann der Code im Kern der Anwendung nicht beeinflusst wird.
Die Structure des Model-View Controller (MVC)
Während der Controller und die Ansicht direkt mit dem Modell gekoppelt sind, ist das Modell nicht direkt mit dem Controller oder der Ansicht gekoppelt.
Verbindlichkeiten von Model-View Controller (MVC)
Erhöhte Komplexität durch die Verwendung separater Ansichts- und Steuerungskomponenten, ohne zugewinn von Flexibilität.
Möglicherweise entstehen übermäßig viele Update-Befehle, beachten Sie hierbei dass nicht alle Ansichten sind immer an allen Änderungen interessiert sind. Es kommt zu „intimen Verbindungen“ zwischen View und Controller.
Benefits von Design Patterns
- Systematische (Software-) Entwicklung
- leichtere Dokumentation durch Expertenwissen
- Verwendung generischer Lösungen
- Erhöhung der Abstraktionsebene
- Name des Design Patter: Eine kurze Mnemonik zur Erweiterung Ihres Designvokabulars.
- Problem: Beschreibung, warum das Muster angewendet werden soll. Eine Bedingungen, die erfüllt sein müssen, bevor das Muster angewendet werden kann
- Lösung: Die Elemente, aus denen das Design besteht, deren Beziehungen, Verantwortlichkeiten und Zusammenarbeit.
- Folgen: Kosten und Nutzen der Anwendung des Musters. Sprach- und Implementierungsprobleme sowie Auswirkungen auf Systemflexibilität, Erweiterbarkeit oder Portabilität. Ziel ist es, ein Muster zu verstehen und zu bewerten.
Template für ein Design Patterns
1. | Name Absicht |
2. | Motivation Anwendung |
3. | Struktur Teilnehmer Zusammenarbeit Implementierung |
4. | Folgen / Konsequenzen |
5. | Bekannte Verwendungen Verwandte Muster |
Um ein verwendetes Entwurfsmuster zu dokumentieren, verwenden Sie die Namen des Musters, um die Rolle der Klasse bei der Implementierung von Mustern anzugeben.
Levels of Consciousness eines Design Pattern
- Unschuld
- Bekannte Tricks
- Kompetente Trickanwendung
- Anwendbarkeit & Konsequenzen bekannt
- Breites Wissen über Muster und deren Interaktion
- Fähigkeit, Wissen in gebildeter Form zu erfassen
Muster ermöglichen den Aufbau hochwertiger Softwarearchitekturen.
Ein Softwareentwurfsmuster beschreibt eine häufig wiederkehrende Struktur interagierender Softwarekomponenten, die ein allgemeines Softwareentwurfsproblem in einem bestimmten Kontext lösen.
Idiome, Designmuster und Architekturmuster helfen Ihnen, wiederkehrende Probleme auf verschiedenen Abstraktionsstufen zu lösen und die Vorteile und Kompromisse sofort zu verstehen. Mithilfe von Mustern können Sie auf einer höheren Abstraktionsstufe über das Design Ihrer Anwendung sprechen.
Das Strategy Design Pattern
Das Strategy Design Pattern unterstützt verschiedene Arten von externen Fremdservices, z.B. zur Berechnung von Steuern. Ebenso unterstützt es verschiedene Arten von Datenbankverbindungen oder wenn unterschiedliche Werte sortiert werden sollen. Definieren Sie eine Familie von Algorithmen, kapseln Sie diese und machen Sie diese austauschbar. Das Strategy Design Pattern lässt den Algorithmus unabhängig von den Clients variieren.
Eine Alternative zum Unterklassen
Unterklassen mischen die Implementierung des Algorithmus mit dem Context. dadurch ist der Kontext schwieriger zu verstehen, zu pflegen und zu erweitern.
- Bei der Verwendung von Unterklassen können wir den Algorithmus nicht dynamisch variieren
- Unterklassen führen zu vielen verwandten Klassen
- Sie unterscheiden sich lediglich in dem verwendeten Algorithmus oder Verhalten.
Einschluss des Algorithmus in Strategie lässt den Algorithmus unabhängig vom Kontext variieren und erleichtert dadurch das Wechseln, Verstehen, Wiederverwenden und Erweitern des Algorithmus.
Wann ist das Strategy Design Pattern anzuwenden?
- bei vielen verwandten Klassen, welche sich nur in ihrem Verhalten unterscheiden, anstatt verschiedene verwandte Abstraktionen zu implementieren.
- Strategy Design Pattern ermöglichen das Konfigurieren einer Klasse mit einem von vielen Verhalten.
- Sie benötigen verschiedene Varianten eines Algorithmus
- Strategy Design Pattern können verwendet werden, wenn Varianten von Algorithmen als Klassenhierarchie implementiert werden.
- eine Klasse definiert viele Verhalten, die in ihren Vorgängen als mehrere bedingte Anweisungen erscheinen
- Verschieben Sie verwandte bedingte Verzweigungen in eine Strategie.
Clients müssen sich der unterschiedlichen Strategien und deren Unterschiede bewusst sein, um die passende Strategie auszuwählen
Aber Clients können auch Implementierungsproblemen ausgesetzt sein.
Strategien sollten nur verwendet werden, wenn eine Verhaltensvariation für den Client relevant ist.
Optionale Strategieobjekte
Kontext prüft, ob eine Strategie vorhanden ist, bevor auf diese zugegriffen wird. Falls eine vorhanden ist, verwenden Sie normalerweise Kontext.
Falls keine Strategie vorhanden ist, führt der Kontext das Standardverhalten aus.
Der Vorteil ist, dass Kunden sich nicht mit Strategieobjekten befassen müssen, es sei denn, sie sind dazu verpflichtet.
- erhöhen Sie die Anzahl der (Strategie-)Objekte
- Manchmal können stateless-Strategien reduziert werden, um den Kontext gemeinsam nutzen können.
- Jeder Status wird von Kontext verwaltet und übergibt diesen für jede Anforderung an das Strategieobjekt. Dadurch entsteht keine oder nur wenige Kopplung zwischen Strategieimplementierungen und Kontext.
- Geteilte Strategien sollten den Status nicht über Aufrufe hinweg aufrechterhalten
Die Strategie-Schnittstelle wird von allen Concrete-Strategy-Klassen gemeinsam genutzt, unabhängig davon, ob die von ihnen implementierten Algorithmen trivial oder komplex sind. Einige ConcreteStrategies verwenden nicht alle Informationen, die an sie weitergegeben werden Simple ConcreteStrategies verwendet möglicherweise keine davon. Der Kontext erstellt bzw. initialisiert auch Parameter, die niemals verwendet werden. Wenn dies ein Problem ist, verwenden Sie eine engere Kopplung zwischen Strategie und Kontext. Lassen Sie die Strategie mehr über den Kontext wissen.
Vergessen Sie aber dabei den Communication Overhead nicht!
Strategiesichtbarkeit für die Kontextinformationen, die die Strategie benötigt; zwei mögliche Strategien:
Übergeben Sie die benötigten Informationen als Parameter …
• Kontext und Strategie entkoppelt
• Kommunikationsaufwand
• Der Algorithmus kann nicht an spezifische Kontextanforderungen angepasst werden
Kontext übergibt sich selbst als Parameter oder Strategie hat einen Bezug zu seinem Kontext …
• Reduzierter Kommunikationsaufwand
• Kontext muss eine komplexere Schnittstelle zu seinen Daten definieren
• Engere Kopplung von Strategie und Kontext
Bei Verwendung des Strategiemusters hängen sowohl die Vorlage als auch die detaillierten Implementierungen von Abstraktionen also den Schnittstellen ab.
Das Composite Design Pattern
Stellen Sie sich einen Zeichnungseditor vor, in dem komplexe Diagramme aus einfachen Komponenten aufgebaut sind und der Benutzer Klassen meist einheitlich behandeln möchte, unabhängig davon, ob sie Grundelemente oder Komponenten darstellen. Also konkretes Beispiel:
• Bild enthält Elemente
• Elemente können gruppiert werden
• Gruppen können andere Gruppen enthalten
- Erstellen Sie Objekte in Baumstrukturen, um (Teil-)Hierarchien darzustellen.
- Das zusammengesetzte Entwurfsmuster ermöglicht es Kunden, einzelne Objekte und Objektzusammensetzungen einheitlich zu behandeln
Verwenden Sie Composite, wenn Sie eine Teilhierarchien von Objekten darstellen möchten oder wenn Clients den Unterschied zwischen einzelnen und zusammengesetzten Objekten ignorieren möchten. Clients werden dann alle Objekte in der Verbundstruktur einheitlich behandeln.
Man unterscheidet zwischen folgende Komponenten:
- Component: Deklariert die Schnittstelle für Objekte in der Komposition und hier wird das entsprechende Standardverhalten implementiert. Oft über eine Schnittstelle für die Zugriffe auf untergeordnete Komponenten und deren Verwaltung deklariert.
- Leaf: Stellt Blattobjekte in der Komposition dar und definiert das primitive Verhalten
- Composite: Speichert „Kinder“ und ist das zusammengesetztes Verhalten
- Client: Greift auf Objekte in der Komposition über die Komponentenschnittstelle zu
Zusammenarbeit der Komponenten
- Clients interagieren mit Objekten über die Komponentenschnittstelle
- Leafes reagieren direkt
- Composites leiten Anforderungen an ihre Kinder weiter und fügen möglicherweise Vor- oder Nachoperationen hinzu
- Primitive Objekte können rekursiv zusammengestellt werden
- Clients können Composites und Primitive einheitlich behandeln. Dabei müssen Clients keine tag-and-case Anweisung / Funktionen schreiben
- Neue Komponenten können einfach hinzugefügt werden
Aber Sie können sich nicht immer auf das Typsystem verlassen, um bestimmte Einschränkungen durchzusetzen; zum Beispiel dass ein Verbund nur bestimmte Komponenten enthält.
Beispiele für Composite Design Pattern:
Computersystem enthält:Treiber, Grafikkarten in den PCIe-Steckplätzen, Speicher und so weiter. Eine solche Teil-Ganz-Struktur kann natürlich mit dem Composite-Muster modelliert werden.
- Iterator: Traverse composite
- Besucher: So lokalisieren Sie Vorgänge, die ansonsten auf Composite- und Leaf-Klassen verteilt sind.
- Verantwortungskette: Verwenden Sie die Komponentenhierarchie zur Aufgabenlösung
- Flyweight: Für das Teilen von Komponenten
Das Composite Design Pattern ermöglicht das Zusammenstellen von Objekten in Baumstrukturen, um Teilhierarchien darzustellen. Wenden Sie das zusammengesetzte Muster an, wenn Clients einzelne Objekte und Objektzusammensetzungen einheitlich behandeln können.
Das Observer Design Pattern
- Präsentationskomponenten, die Ansichten im Dokument darstellen sollen, sollten von den Datenstrukturen des Kernstruktur des Dokuments getrennt werden.
- Mehrere Ansichten des Dokuments sollten auch gleichzeitig möglich sein. Sie müssen Updates verwalten, die das Dokument präsentieren
Die objektorientierte Programmierung fördert das Aufteilen von Problemen in Objekte mit kleinen Verantwortlichkeiten (idealerweise eine), die jedoch zusammenarbeiten können, um komplexe Aufgaben zu lösen.
Der klare Vorteil besteht in der erleichterten Implementierung und Wartung jedes Objekts, ist wiederverwendbar und ermöglicht flexible Kombinationen. Nachteil ist jedoch, dass das Verhalten auf mehrere Objekte verteilt ist. Jede Zustandsänderung eines Objekts wirkt sich häufig auf viele andere aus.
Die Weitergabe von Änderungen von Objektzuständen kann in Objekten fest verdrahtet sein, dies bindet die Objekte zusammen und verringert deren Flexibilität und Wiederverwendbarkeit. Es ist ein flexibler Weg erforderlich, damit Objekte sich über Änderungen informieren können, ohne diese stark zu koppeln. Prototypische Anwendung: Trennung der GUI von den zugrunde liegenden Daten, sodass Klassen, die Anwendungsdaten und Präsentationen definieren, unabhängig voneinander wiederverwendet werden können.
Kommunikation ohne Kopplung
Die Aufgabe besteht darin, ein Datenmodell als Subjekt von „Parteien“, die an Änderungen des internen Zustands interessiert sind zu entkoppeln.
Die Durchführung erfolgt dadurch, dass das Subjekt keine Informationen über seine Beobachter erhalten sollte. Die Identität und Anzahl der Beobachter ist nicht vorbestimmt und in der Zukunft können dem System neuartige Empfängerklassen hinzugefügt werden.
Polling ist unangemessen da zu ineffizient.
Definieren Sie eine One-to-many-Abhängigkeit zwischen Objekten. Wenn ein Objekt seinen Status ändert, werden alle abhängigen Objekte automatisch benachrichtigt und aktualisiert.
- Subject: kennt seinen Beobachter und bietet Funktionen zum Anbringen und Entfernen von Observer-Objekten
- Observer: Definiert eine Aktualisierungsoberfläche zur Unterstützung der Benachrichtigung über Änderungen in einem Betreff
- ConcreteSubject: speichert den „Status of interest“ für ConcreteObserver-Objekte und sendet eine Benachrichtigung an seine Beobachter bei Zustandswechsel
- ConcreteObserver: verwaltet einen Verweis auf ein ConcreteSubject-Objekt und speichert den Zustand, der dem Betreff entsprechen soll. Und implementiert die Observer-Aktualisierungsschnittstelle.
Abstrakte Kopplung zwischen Betreff und Beobachter
• Unterstützung für Broadcast-Kommunikation:
• benachrichtigen gibt keinen Empfänger an
• Der Absender kennt den (konkreten) Empfängertyp nicht
Nachteile sind:
• Unerwartete / unkontrollierte Updates
• Gefahr von Aktualisierungskaskaden für Beobachter und ihre abhängigen Objekte
• Aktualisierung an alle Beobachter gesendet, auch wenn einige von ihnen möglicherweise nicht an der jeweiligen Änderung interessiert sind
• Kein Detail, was sich im Thema geändert hat; Beobachter müssen möglicherweise hart arbeiten, um herauszufinden, was sich geändert hat
• Eine gemeinsame Aktualisierungsschnittstelle für alle Beobachter begrenzt die Kommunikationsschnittstelle: Der Betreff kann keine optionalen Parameter an Beobachter senden
Methoden der Observer Design Pattern
- addObserver(Observer) Fügt der Beobachterliste einen Beobachter hinzu
- clearChanged() Löscht eine beobachtbare Änderung
- countObservers() Zählt die Anzahl der Beobachter
- deleteObserver(Observer) Löscht einen Beobachter aus der Beobachterliste
- deleteObservers() Löscht Beobachter aus der Beobachterliste
- hasChanged() Gibt einen echten Booleschen Wert zurück, wenn eine beobachtbare Änderung vorgenommen wurden oder auftritt
- notifyObservers() Benachrichtigt alle Beobachter über eine beobachtbare Änderung
- notifyObservers(Object) Benachrichtigt alle Beobachter über die angegebene beobachtbare Änderung
- setChanged() Legt ein Flag fest, um eine beobachtbare Änderung zu beachten
- void update(Observable o, Object arg) Diese Methode wird aufgerufen, wenn das beobachtete Objekt geändert wird. Eine Anwendung ruft die notifyObservers-Methode eines beobachtbaren Objekts auf, um alle Beobachter des Objekts über die Änderung zu informieren. Die Parameter: O – stehen dabei das beobachtete Objekt und arg – ein an die notifyObservers-Methode übergebenes Argument.
Die normalen addObserver(Observer) Methode wird, um die Art der Ereignisse zu spezifizieren und um jene an denen der Observer interessiert ist, zu addObserver(Observer, Aspect) erweitert. Wobei Aspect den Ereignistyp codiert, an dem der Beobachter interessiert ist.
Wenn sich der Status des Betreffs ändert, sendet der Betreff selbst eine Nachricht triggerUpdateForEvent(anAspect).
Das Factory Method Design Pattern
Nehmen wir an, wir möchten ein Framework für Anwendungen entwickeln, das dem Benutzer mehrere Dokumente präsentieren kann also im MDI-Stil. Das heisst, wir möchten eine Vielzahl von Anwendungen unterstützen: Texteditor, Textverarbeitungen, Vektorzeichenanwendungen, Dokumentbetrachter und so weiter.
Unser Framework sollte insbesondere in der Lage sein, die Dokumente zu verwalten.
Definieren Sie eine Schnittstelle zum Erstellen eines Objekts, lassen Sie die Unterklassen jedoch entscheiden, welche Klasse instanziiert werden soll. Mit der Factory-Methode kann eine Klasse die Instantiierung auf Unterklassen verschieben.
- Product: Definiert die Schnittstelle der Objekte, die die Factory-Methode erstellt.
- ConcreteProduct: Implementiert die Produktschnittstelle.
- Creator:
- Deklariert die Factory-Methode, die ein Objekt vom Typ Product zurückgibt. DerCreator kann auch eine Standardimplementierung von Factory-Methode, die ein ConcreteProduct-Standardobjekt zurückgeben definieren.
- ConcreteCreator: Überschreibt die Factory-Methode, um eine Instanz von einem Konkreten Product zurückzugeben.
Der Code des Frameworks behandelt nur die Produktschnittstelle. Daher kann es mit jeder benutzerdefinierten ConcreteProduct-Klasse arbeiten. Bietet einen Hook für Unterklassen. Der Hook kann zum Bereitstellen einer erweiterten Version eines Objekts verwendet werden. Dabei gibt es zwei Hauptvarianten:
• Der Creator ist abstrakt
• Der Creator ist konkret und bietet eine angemessene Standardimplementierung
Factory-Methoden werden normalerweise innerhalb von Template-Methoden aufgerufen. und Abstract Factory wird oft mit Factory-Methoden implementiert.
Das Abstract Factory Design Pattern
Wird dazu verwandtet Klassen zu erstellt, die eine oder mehrere gemeinsame Schnittstellen implementieren.
Ein Beispiel ist, verschiedene Datenbanken zu unterstützen.
Bedarf:
• Die Anwendung sollte mehrere Datenbanken unterstützen
(Wir möchten die Datenbank zum Startzeitpunkt ändern können.)
• Wir möchten weitere Datenbanken unterstützen
(Wir möchten, dass die Implementierung der spezifischen Datenbank (en) nicht bekannt ist.)
Dazu wird eine gemeinsame Schnittstelle aus den konkreten Klassen abstrahiert. Um die Abstraktion der Datenbank abzuschließen, müssen Sie auch Klassenhierarchien für CallableStatements, PreparedStatements, Blobs usw. erstellen. Der mit der Datenbank interagierende Code kann jetzt mit ResultSets und SQL-Anweisungen umgehen, ohne sich auf die konkreten Klassen zu beziehen, z. B. Firebird-ResultSet.
Jedoch muss die konkrete Implementierungsunterklasse zum Zeitpunkt der Erstellung bekannt sein!
- AbstractFactory: Bietet eine Schnittstelle zum Erstellen von Produkten einer Familie
- ConcreteFactory :Implementiert die Operationen zur Erstellung konkreter Produkte
- AbstractProduct: Deklariert die Schnittstelle für konkrete Produkte
- ConcreteProduct: bietet eine Implementierung für das von der entsprechende ConcreteFactory
- Client: Erstellt Produkte durch Aufruf der ConcreteFactory und verwendet die AbstractProduct-Schnittstelle
- Abstracts weg von konkret products, Clienten können nicht wissen, welches konkrete Product sie verwenden, selbst zum Zeitpunkt der Erstellung.
- Der Austausch von Produktfamilien ist einfach, da das Ändern einer Zeile das Verhalten einer ganzen Produktfamilie vollständig vertauschen kann.
- Gewährleistet die Konsistenz zwischen den Products, da sich die Familienauswahl auf eine Zeile konzentriert, können Produkttypen nicht versehentlich gemischt werden.
- Die Unterstützung neuer Produkte ist schwierig, da beim Hinzufügen neuer Products die abstrakte fabric und alle ihre Unterklassen geändert werden müssen.
- Die Erstellung von Objekten ist kein Standard, Clients müssen wissen, dass sie die Factory anstelle eines Konstruktors verwenden.
Nur eine Codezeile (oder Konfigurationsparameter) muss variieren, um verschiedene Datenbanken unterstützen zu können. Auch ermöglicht es eine Datenbank aus verschiedener Datenbanken beim Start auszuwählen
Auch zwingt die Abstaktion die Schaffung konsistenter Produktfamilien.
Der Code muss einer neuen Konvention zum Erstellen von Produkten aus einer Familie folgen (anstatt den Standardkonstruktor zu verwenden.)
Erzwingen von Singularität
In einigen Fällen ist ein Mechanismus erforderlich, um die Singularität von Objekten durchzusetzen. Dadurch erzwingt man, dass höchstens eine Instanz einer Klasse zur Laufzeit vorhanden ist. In einem System sollte es beispielsweise nur einen Druckerspooler geben. Oder es sollte nur eine Klasse für Interaktionen mit der Datenbank vorhanden sein.
Hierfür gibt es zwei Muster zur Durchsetzung der Singularität:
• Singleton
• Monostatus
Achtung: Es sollte nur eine Drucker-Spool-Instanz eines Objekts sein, das den Konstruktor zum Zeitpunkt der Programminitialisierung verwendet und dieses Objekt verwendet.
Stellen Sie sicher, dass eine Klasse nur eine Instanz hat, und stellen Sie einen globalen Zugriffspunkt zur Verfügung. Dabei sollte der Constructor
privat oder geschützt sein.
Vorteile:
Plattformübergreifende Verwendung geeigneter Middleware
Singleton kann für viele JVMs erweitert werden.
• Gilt für jede Klasse
• Kann durch „derivation“ erstellt werden
Wenn Sie eine Klasse angeben, können Sie eine Unterklasse erstellen, die ein Singleton ist.
Preis
• „Faule“ Creator
(Kontrollierter Zugriff auf die Einzelinstanz.)
Wenn der Singleton nie verwendet wird, wird er niemals erstellt.
• Zerstörung ist undefiniert
• nicht vererbt
Eine von einem Singleton abgeleitete Klasse ist kein Singleton.
• Nicht transparent
Benutzer eines Singleton wissen, dass sie ein Singleton verwenden.
„Software design patterns erfassen bewährte und erfolgreiche Designlösungen. Zu den unterschiedlichen Ansichten von Designmustern gehört, dass sie geschaffen werden, um Designdefizite in Programmiersprachen auszugleichen. Das heißt, Designmuster sind erforderlich, wenn Programmiersprachen die Aufgabe nicht auf einfache Weise erledigen können… “
The Monostate Design Pattern
Vorteile
- Transparent: Der Benutzer muss nicht wissen, dass das Objekt Monostate ist. Zerstörung ist klar definiert.
- Ableitbarkeit: Derivate eines Monostats sind Monostate; Derivate eines Monostats sind Teil desselben Monostats.
- Polymorphismus: Da die Methoden eines Monostats nicht statisch sind, können sie in einer Ableitung überschrieben werden. Ableitungen können bei gleichen statischen Variablen ein unterschiedliches Verhalten aufweisen.
Preis
- Keine Konvertierung: Eine normale Klasse kann nicht in ein Monostat umgewandelt werden.
- Effizienz: Ein Monostatiker kann viele Kreationen und Zerstörungen durchlaufen, weil er ein reales Objekt ist.
- Präsenz: Die Variablen eines Monostats beanspruchen Platz, auch wenn der Monostate niemals verwendet wird.
- Plattform lokal: Ein Monostate kann nicht über mehrere JVM-Instanzen oder über mehrere Plattformen hinweg arbeiten.
Singleton wird am besten verwendet, wenn Sie über eine vorhandene Klasse verfügen, die Sie durch Derivierung einschränken möchten, und Sie nicht daran denken, dass jeder die instance() – Methode aufrufen muss, um Zugriff zu erhalten.
Monostate wird am besten verwendet, wenn die Singularität der Klasse für die Benutzer transparent sein soll oder wenn polymorphe Ableitungen des einzelnen Objekts verwendet werden sollen.
The Observer Design Pattern
Definieren Sie eine Eins-zu-Viele-Abhängigkeit zwischen Objekten, damit alle abhängigen Objekte eines Objekts automatisch benachrichtigt und aktualisiert werden, wenn sich der Status eines Objekts ändert.
Wir möchten die Entscheidung zwischen Beobachtern im Push- oder Pull-Modus vermeiden und Beobachter besser unterstützen, die nur an bestimmten Ereignissen interessiert sind.
Alternative Implementation unter AspectJ
- Die Existenz von Subjekt- und Beobachterrollen
(d. h. die Tatsache, dass einige Klassen als Beobachter und einige als Subjekte fungieren) - Pflege eines Mappings von Probanden zu Beobachtern
- Die allgemeine Aktualisierungslogik: Betreffänderungen lösen Observer-Aktualisierungen aus
- Welche Klassen können Fächer sein und welche können Beobachter sein
- Eine Reihe von interessierenden Änderungen an den Betreffs, die Aktualisierungen für die Beobachter auslösen
- Spezifische Mittel zum Aktualisieren jeder Art von Observer, wenn die Aktualisierungslogik dies erfordert
- Lokalität: Der gesamte Code, der das Observer-Muster implementiert, ist in den abstrakten und konkreten Beobachteraspekten enthalten, keiner davon in den Teilnehmerklassen. Es gibt keine Kopplung zwischen den Teilnehmern. Mögliche Änderungen an jeder Observer-Musterinstanz sind auf eine Stelle beschränkt.
- Wiederverwendbarkeit: Der Kernmustercode ist abstrahiert und wiederverwendbar. Die Implementierung von ObserverProtocol verallgemeinert das allgemeine Musterverhalten. Der abstrakte Aspekt kann wiederverwendet und für mehrere Observer-Musterinstanzen gemeinsam genutzt werden.
- Kompositionstransparenz: Da die Implementierung eines Musterteilers nicht an das Muster gekoppelt ist, wird der Code nicht komplizierter, wenn ein Betreff oder Beobachter an mehreren Beobachtungsbeziehungen teilnimmt, werden die Musterinstanzen nicht verändert. Jede Instanz des Musters kann unabhängig voneinander behandelt werden.
- (Nicht-)Erweiterbarkeit: Es ist möglich, zwischen der Verwendung eines Musters und nicht der Verwendung im System zu wechseln.
Wie es implementiert wird, hängt von den verfügbaren Mechanismen der Programmiersprache ab! Somit besteht immer eine Verbindung zuwischen Programmiersprachen und Design Pattern.
Antworten