2.1 Adapter

Adapter (Adapter) ist ein strukturelles Entwurfsmuster, das die Verwendung der Funktionen eines Objekts organisieren soll, das nicht über eine speziell erstellte Schnittstelle geändert werden kann.

Die offizielle Definition ist etwas knifflig, aber wenn Sie es mit Ihren eigenen Worten ausdrücken, ist ein Adapter ein Entwurfsmuster, das die Zusammenarbeit von Objekten mit inkompatiblen Schnittstellen ermöglicht .

Adaptermuster

Wird verwendet , um die Nutzung der Funktionen eines Objekts zu organisieren, das nicht über eine speziell erstellte Schnittstelle geändert werden kann. Es wird eine zusätzliche Klasse erstellt, die über die erforderliche Schnittstelle verfügt, und diese Klasse wiederum ruft die Methoden des gewünschten Objekts auf (das nicht über die erforderliche Schnittstelle verfügt).

Wichtig! Wenn Sie im Code das Suffix Adapter für eine Klasse finden, können Sie zu Recht davon ausgehen, dass diese Klasse als Adapter fungiert und einer Gruppe von Klassen zugeordnet ist, die nach dem oben beschriebenen Schema arbeiten.

Es wird in Fällen verwendet , in denen das System die erforderlichen Daten und Verhaltensweisen unterstützt, aber über eine ungeeignete Schnittstelle verfügt. Das Adaptermuster wird am häufigsten verwendet, wenn Sie eine Klasse erstellen möchten, die von einer neuen oder vorhandenen abstrakten Klasse erbt.

Starke Seiten:

  • Der Übergang zur Verwendung anderer externer Klassen erfordert keine Überarbeitung des Systems selbst, es reicht aus, eine weitere Adapterklasse zu implementieren.
  • Unabhängigkeit von der Implementierung externer Klassen (Klassen aus Bibliotheken, deren Code wir nicht ändern können). Ihr Programm wird unabhängig von der Schnittstelle externer Klassen.

2.2 Dekorateure

Decorator ist ein strukturelles Entwurfsmuster zum dynamischen Anhängen zusätzlicher Verhaltensweisen an ein Objekt. Das Decorator-Muster bietet eine gute und flexible Alternative zur Praxis der Unterklassenbildung zur Erweiterung der Funktionalität.

Dekorationsmuster

Wird verwendet, um zusätzliche Verpflichtungen dynamisch mit einem Objekt zu verbinden.

Viele von Ihnen werden sich fragen: Wie können Sie einem Objekt dynamisch (während das Programm läuft) neues Verhalten hinzufügen? Ein Objekt kann aus Teilen, also kleinen Objekten, zusammengesetzt werden. Erinnern Sie sich an Filterketten in Servlets? Oder die Stream-API, als Sie eine Abfrage mit filter(), map(), list() geschrieben haben?

IntStream.of(50, 60, 70, 80, 90).filter(x -> x < 90).map(x -> x + 10).limit(3).forEach(System.out::print);

Stärken des Decorator-Musters:

  • Es ist nicht erforderlich, Unterklassen zu erstellen, um die Funktionalität eines Objekts zu erweitern.
  • Die Möglichkeit, neue Funktionalität überall dynamisch zu verbinden: vor oder nach der Hauptfunktionalität des ConcreteComponent-Objekts.

2.3 Proxys

Proxy ist ein strukturelles Entwurfsmuster, das ein Objekt bereitstellt, das den Zugriff auf ein anderes Objekt steuert und alle seine Aufrufe abfängt und weiterleitet.

Stellvertreter (Proxy)

Das Proxy-Muster stellt ein Ersatzobjekt anstelle des realen Objekts bereit. Dieses Objekt steuert den Zugriff auf das Originalobjekt. Sehr oft verwendet.

Erinnern Sie sich, wie wir das Mockito-Framework verwendet und einen Aufruf eines realen Objekts mithilfe der Methode Mockito.spy() oder der Annotation @Spy abgefangen haben? Zu diesem Zeitpunkt wurde ein spezielles Proxy-Objekt erstellt, über das alle Aufrufe des ursprünglichen Objekts erfolgten.

Und dann könnten wir diese Aufrufe verwalten, indem wir dem Objekt Regeln hinzufügen. Das ist richtig – das ursprüngliche Objekt ändert sich nicht und die Arbeit damit wird viel flexibler. Dies ist besonders nützlich, wenn wir das Proxy-Objekt nicht aus unserem Code aufrufen, sondern irgendwo übergeben. So steuern wir die Kommunikation zweier Objekte unabhängig von uns.

Arten von Proxys nach Zweck:

  • Protokollierungs-Proxy : Protokolliert alle Aufrufe des „Subject“ mit ihren Parametern.
  • Remote-Proxy (Remote-Proxys): Ermöglicht die Kommunikation mit dem „Subjekt“, das sich in einem anderen Adressraum oder auf einem Remote-Computer befindet. Möglicherweise ist es auch dafür verantwortlich, die Anfrage und ihre Argumente zu kodieren und die kodierte Anfrage an den echten „Betreff“ zu senden.
  • Virtueller Proxy (virtuelle Proxys): stellt sicher, dass das echte „Subjekt“ nur dann erstellt wird, wenn es wirklich benötigt wird. Es kann auch einige Informationen über das tatsächliche „Subjekt“ zwischenspeichern, um dessen Erstellung zu verzögern.
  • Copy-on-Write : Stellt eine Kopie des „Subjekts“ bereit, wenn der Client bestimmte Aktionen ausführt (ein Sonderfall des „virtuellen Proxys“).
  • Schutz-Proxys : Kann prüfen, ob der Anrufer über die erforderlichen Berechtigungen zum Stellen der Anfrage verfügt.
  • Caching-Proxy : Bietet eine vorübergehende Speicherung von Berechnungsergebnissen, bevor diese an mehrere Clients weitergegeben werden, die die Ergebnisse teilen können.
  • Screening-Proxy: Schützt das „Subjekt“ vor gefährlichen Clients (oder umgekehrt).
  • Synchronisations-Proxy : Führt eine synchronisierte Zugriffskontrolle auf das „Subjekt“ in einer asynchronen Multithread-Umgebung durch.
  • „Intelligenter“ Link (Smart Reference Proxy): Führt zusätzliche Aktionen aus, wenn ein Link zum „Betreff“ erstellt wird, berechnet beispielsweise die Anzahl der aktiven Links zum „Betreff“.

2.4 Brücke

Das Bridge-Muster ist ein strukturelles Entwurfsmuster, das verwendet wird, um „Abstraktion und Implementierung zu trennen, damit sie sich unabhängig voneinander ändern können“.

Das Brückenmuster verwendet Kapselung und Aggregation und kann Vererbung nutzen, um die Verantwortung zwischen Klassen aufzuteilen.

Brücke

Wenn Abstraktion und Implementierung getrennt werden, können sie sich unabhängig voneinander ändern. Mit anderen Worten: Bei der Implementierung über das Brückenmuster beeinträchtigt die Änderung der Struktur der Schnittstelle nicht die Änderung der Struktur der Implementierung.

Betrachten Sie eine solche Abstraktion als Figur. Es gibt viele Arten von Formen, jede mit ihren eigenen Eigenschaften und Methoden. Es gibt jedoch etwas, das alle Figuren vereint. Beispielsweise muss jede Form in der Lage sein, sich selbst zu zeichnen, zu skalieren usw.

Gleichzeitig können sich die Zeichengrafiken je nach Betriebssystemtyp oder Grafikbibliothek unterscheiden. Formen sollten in der Lage sein, sich in verschiedenen Grafikumgebungen selbst zu zeichnen. Es ist jedoch unpraktisch, alle Zeichenmethoden in jeder Form zu implementieren oder die Form jedes Mal zu ändern, wenn sich die Zeichenmethode ändert.

In diesem Fall hilft das Bridge-Muster, das es Ihnen ermöglicht, neue Klassen zu erstellen, die das Zeichnen in verschiedenen grafischen Umgebungen implementieren. Mit diesem Ansatz ist es sehr einfach, sowohl neue Formen als auch Möglichkeiten zum Zeichnen dieser Formen hinzuzufügen.

Die durch den Pfeil in den Diagrammen dargestellte Verbindung kann zwei Bedeutungen haben: a) „eine Art“, gemäß dem Liskov-Substitutionsprinzip, und b) eine der möglichen Implementierungen der Abstraktion. Sprachen verwenden typischerweise Vererbung, um sowohl a) als auch b) zu implementieren, was dazu neigt, Klassenhierarchien aufzublähen.

Die Brücke dient genau der Lösung dieses Problems: Objekte werden paarweise aus einem Objekt einer Klasse der Hierarchie A und Hierarchie B erstellt, Vererbung innerhalb der Hierarchie A hat nach Liskov die Bedeutung von „Varietät“ und für den Begriff „Implementierung“. „Abstraktion“ wird eine Verbindung von Objekt A zu seinem gepaarten Objekt B verwendet.

2.5 Fassade

Das Fassadenmuster ist ein strukturelles Entwurfsmuster, das die Komplexität eines Systems verbirgt, indem es alle möglichen externen Aufrufe auf ein einzelnes Objekt reduziert, das sie an die entsprechenden Objekte im System delegiert.

Fassadenvorlage

Wie kann eine einheitliche Schnittstelle mit einer Reihe unterschiedlicher Implementierungen oder Schnittstellen bereitgestellt werden, beispielsweise zu einem Subsystem, wenn eine starke Kopplung an dieses Subsystem unerwünscht ist oder sich die Implementierung des Subsystems ändern könnte?

Definieren Sie einen Interaktionspunkt mit dem Subsystem – ein Fassadenobjekt, das eine gemeinsame Schnittstelle mit dem Subsystem bereitstellt, und weisen Sie ihm die Verantwortung für die Interaktion mit seinen Komponenten zu. Eine Fassade ist ein externes Objekt, das einen einzigen Einstiegspunkt für Subsystemdienste bietet.

Die Implementierung anderer Subsystemkomponenten ist privat und für externe Komponenten nicht sichtbar. Das Fassadenobjekt stellt eine änderungsresistente Implementierung des GRASP-Musters im Hinblick auf den Schutz vor Änderungen in der Implementierung des Subsystems bereit.

Wichtig! Dieses Muster wird verwendet, wenn wir eine Gruppe von Objekten vollständig verbergen und die gesamte Kommunikation mit ihnen über unser Objekt weiterleiten möchten. Wenn Sie lediglich eine gewisse Kontrolle über den Kommunikationsprozess von Objekten ermöglichen und diese nicht unbedingt verbergen möchten, ist es besser, das Proxy-Muster zu verwenden.