Entwurfsmuster sind Lösungen auf Entwurfsebene für wiederkehrende Probleme, auf die wir Softwareingenieure häufig stoßen. Es ist kein code, ich wiederhole, ❌CODE. Es ist wie eine Beschreibung, wie man diese Probleme angehen und eine Lösung entwerfen kann.

Die Verwendung dieser Muster wird als gute Praxis angesehen, da das Design der Lösung ziemlich erprobt ist, was zu einer höheren Lesbarkeit des endgültigen Codes führt., Designmuster werden häufig für OOP-Sprachen wie Java erstellt und von diesen verwendet, in denen die meisten Beispiele von hier aus geschrieben werden.

Arten von Entwurfsmustern

Derzeit werden etwa 26 Muster entdeckt (ich glaube kaum, dass ich sie alle machen werde…).

Diese 26 können in 3 Typen eingeteilt werden:

1. Creational: Diese Muster sind für die Klasseninstanziierung konzipiert. Sie können entweder Klassenerstellungsmuster oder Objekterstellungsmuster sein.

2. Strukturell: Diese Muster sind in Bezug auf die Struktur und Zusammensetzung einer Klasse konzipiert., Das Hauptziel der meisten dieser Muster ist es, die Funktionalität der beteiligten Klasse(en) zu erhöhen, ohne viel von ihrer Zusammensetzung zu ändern.

3. Behavioral: Diese Muster sind so konzipiert, je nachdem, wie eine Klasse mit anderen kommuniziert.

In diesem Beitrag werden wir für jeden klassifizierten Typ ein grundlegendes Entwurfsmuster durchgehen.

Typ 1: Creational-Das Singleton-Entwurfsmuster

Das Singleton-Entwurfsmuster ist ein Kreationsmuster, dessen Ziel es ist, nur eine Instanz einer Klasse zu erstellen und nur einen globalen Zugangspunkt zu diesem Objekt bereitzustellen., Ein häufig verwendetes Beispiel für eine solche Klasse in Java ist Calendar, bei dem Sie keine Instanz dieser Klasse erstellen können. Es verwendet auch eine eigene getInstance() – Methode, um das zu verwendende Objekt abzurufen.

Eine Klasse, die das Singleton-Entwurfsmuster verwendet, enthält

Singleton-Klassendiagramm
  1. Eine private statische Variable, die die einzige Instanz der Klasse enthält.
  2. Ein privater Konstruktor, daher kann er nirgendwo anders instanziiert werden.
  3. Eine öffentliche statische Methode, um die einzelne Instanz der Klasse zurückzugeben.,

Es gibt viele verschiedene Implementierungen des Singleton-Designs. Heute werde ich die Implementierungen von;

1. Eifrig Instanziierung

2. Verzögerte Instanziierung

3. Thread-sichere Instanziierung

Eifrige Instanziierung

public class EagerSingleton {// create an instance of the class.private static EagerSingleton instance = new EagerSingleton();// private constructor, so it cannot be instantiated outside this class.private EagerSingleton() { }// get the only instance of the object created.public static EagerSingleton getInstance() {return instance;}}

Diese Art der Instanziierung tritt während des Ladens der Klasse auf, da die Instanziierung der Variableninstanz außerhalb einer Methode erfolgt. Dies stellt einen erheblichen Nachteil dar, wenn diese Klasse von der Clientanwendung überhaupt nicht verwendet wird. Der Notfallplan, wenn diese Klasse nicht verwendet wird, ist die faule Instanziierung.,

Lazy Days

Es gibt keinen großen Unterschied zur obigen Implementierung. Die Hauptunterschiede bestehen darin, dass die statische Variable anfänglich als null deklariert wird und nur innerhalb der getInstance() – Methode instanziiert wird, wenn die Instanzvariable zum Zeitpunkt der Überprüfung null bleibt.

Dies behebt ein Problem, aber ein anderes existiert noch. Was ist, wenn zwei verschiedene Clients gleichzeitig bis zur Millisekunde auf die Singleton-Klasse zugreifen?, Nun, sie werden überprüfen, ob die Instanz gleichzeitig null ist, und sie werden es wahr finden, und so werden zwei Instanzen der Klasse für jede Anforderung der beiden Clients erstellt. Um dies zu beheben, soll eine Thread-sichere Instanziierung implementiert werden.

(Thread) Sicherheit ist der Schlüssel

In Java wird das Schlüsselwort synchronized für Methoden oder Objekte verwendet, um die Thread-Sicherheit zu implementieren, sodass nur ein Thread gleichzeitig auf eine bestimmte Ressource zugreift. Die Klasseninstanziierung wird in einen synchronisierten Block eingefügt, sodass auf die Methode jeweils nur von einem Client zugegriffen werden kann.,

Der Overhead für die synchronisierte Methode ist hoch und verringert die Leistung des gesamten Vorgangs.

Wenn beispielsweise die Instanzvariable bereits instanziiert wurde, wird jedes Mal, wenn ein Client auf die Methode getInstance() zugreift, die Methode synchronized ausgeführt und die Leistung sinkt. Dies geschieht nur, um zu überprüfen, ob der Wert der instance Variablen null ist. Wenn es feststellt, dass es ist, verlässt es die Methode.

Um diesen Overhead zu reduzieren, wird eine doppelte Verriegelung verwendet., Die Prüfung wird auch vor dersynchronized – Methode verwendet, und wenn der Wert allein null ist, wird die synchronized – Methode ausgeführt.

Nun zur nächsten Klassifikation.

Typ 2: Strukturelle – Das Decorator Design Pattern

ich bin gonna geben Sie einen kleinen Szenario zu geben, einen besseren Kontext, warum und wo sollten Sie mit dem Decorator-Muster.

Angenommen, Sie besitzen ein Café, und wie jeder Neuling beginnen Sie mit nur zwei Arten von einfachem Kaffee, The House Blend und Dark Roast., In Ihrem Abrechnungssystem gab es eine Klasse für die verschiedenen Kaffeemischungen, die die abstrakte Getränkeklasse erbt. Die Leute beginnen tatsächlich zu kommen und haben Ihre wunderbare (wenn auch bitter?) Kaffee. Dann gibt es die Kaffeebohnen, die, Gott bewahre, Zucker oder Milch wollen. So eine Travestie für Kaffee!! ??

Jetzt müssen Sie auch diese beiden Add-Ons haben, sowohl für das Menü als auch leider für das Abrechnungssystem. Ursprünglich, Ihre IT-Person wird eine Unterklasse für beide Kaffees machen, eine einschließlich Zucker, die andere Milch., Dann, da die Kunden immer Recht haben, sagt man diese gefürchteten Worte:

“ Kann ich bitte einen Milchkaffee mit Zucker bekommen?“

???

Es geht Ihr Abrechnungssystem lachen in Ihrem Gesicht wieder. Nun, zurück zum Reißbrett….

Die IT-Person fügt dann Milchkaffee mit Zucker als eine weitere Unterklasse zu jeder übergeordneten Kaffeeklasse hinzu. Der Rest des Monats ist reibungsloses Segeln, Die Leute stehen Schlange, um Ihren Kaffee zu trinken, Sie verdienen tatsächlich Geld. ??

Aber warte, da ist noch mehr!

Die Welt ist wieder gegen dich., Ein Konkurrent öffnet sich auf der anderen Straßenseite, mit nicht nur 4 Kaffeesorten, sondern auch mehr als 10 Add-Ons! ?

Sie kaufen all diese und mehr, um selbst besseren Kaffee zu verkaufen, und denken Sie dann daran, dass Sie vergessen haben, dieses Dratted-Abrechnungssystem zu aktualisieren. Sie können möglicherweise nicht die unendliche Anzahl von Unterklassen für alle Kombinationen aller Add-Ons erstellen, auch mit den neuen Kaffeemischungen. Ganz zu schweigen von der Größe des endgültigen Systems.??

Zeit, um tatsächlich in ein ordnungsgemäßes Abrechnungssystem zu investieren., Sie finden neue IT-Mitarbeiter, die tatsächlich wissen, was sie tun und sie sagen;

„Warum, das wird so viel einfacher und kleiner, wenn es das Dekoratormuster verwendet.“

Was ist denn das?

Das Dekorator-Entwurfsmuster fällt in die strukturelle Kategorie, die sich mit der tatsächlichen Struktur einer Klasse befasst, sei es durch Vererbung, Zusammensetzung oder beides. Ziel dieses Designs ist es, die Funktionalität eines Objekts zur Laufzeit zu ändern. Dies ist eines der vielen anderen Entwurfsmuster, die abstrakte Klassen und Schnittstellen zur Komposition verwenden, um das gewünschte Ergebnis zu erzielen.,

Lass uns Mathe eine Chance geben (Schauder?) zu bringen, all dies in die Perspektive;

Nehmen Sie 4 Kaffee-Mischungen und-10-add-ons. Wenn wir an der Erzeugung von Unterklassen für jede andere Kombination aller Add-Ons für eine Kaffeesorte festhalten. Das ist;

(10-1)2 = 92 = 81 Unterklassen

Wir subtrahieren 1 von den 10, da Sie ein Add-On nicht mit einem anderen desselben Typs kombinieren können, Zucker mit Zucker klingt dumm. Und das ist nur für eine Kaffeemischung. Multiplizieren Sie diese Zahl mit 4 und Sie erhalten satte 324 verschiedene Unterklassen!, Sprechen Sie über all diese Codierung…

Aber mit dem Dekoratormuster werden in diesem Szenario nur 16 Klassen benötigt. Willst du wetten?

Dekorateur Design Muster Klasse diagramm
Klasse diagramm nach kaffee shop szenario

Wenn wir karte heraus unsere szenario nach der klasse diagramm oben, wir erhalten 4 Klassen für die 4 Kaffeemischungen, 10 für jedes Add-On und 1 für die abstrakte Komponente und 1 mehr für den abstrakten Dekorateur. Siehe! 16!, Jetzt übergebt die $ 100.?? (jk, aber es wird nicht abgelehnt, wenn es gegeben wird… nur zu sagen)

Wie Sie oben sehen können, erbt die AddOn-Abstract-Klasse, so wie die konkreten Kaffeemischungen Unterklassen der Getränke-Abstract-Klasse sind, auch ihre Methoden davon. Die Add-Ons, dh ihre Unterklassen, erben wiederum alle neuen Methoden, um dem Basisobjekt bei Bedarf Funktionalität hinzuzufügen.

Kommen wir zur Codierung, um dieses verwendete Muster zu sehen.,

Zuerst, um die abstrakte Getränkeklasse zu erstellen, von der alle verschiedenen Kaffeemischungen erben:

Dann, um beide konkreten Kaffeemischungsklassen hinzuzufügen.

Die abstrakte AddOn-Klasse erbt auch von der abstrakten Addon-Klasse (mehr dazu unten).

Und jetzt die konkreten Implementierungen dieser abstrakten Klasse:

Wie Sie oben sehen können, können wir jede Unterklasse von Addon an jede Unterklasse von AddOn übergeben und die zusätzlichen Kosten sowie die aktualisierte Beschreibung erhalten. Und da die AddOn-Klasse im Wesentlichen vom Typ Getränke ist, können wir ein AddOn an ein anderes AddOn übergeben., Auf diese Weise können wir einer bestimmten Kaffeemischung eine beliebige Anzahl von Add-Ons hinzufügen.

Jetzt etwas Code schreiben, um dies zu testen.

Das Endergebnis ist:

PS Dies ist in Sri Lanka Rupien

Es funktioniert! Wir konnten einer Kaffeemischung mehr als ein Add-On hinzufügen und ihre endgültigen Kosten und Beschreibungen erfolgreich aktualisieren, ohne dass für jede Add-On-Kombination für alle Kaffeemischungen unendliche Unterklassen erstellt werden mussten.

Schließlich zur letzten Kategorie.,

Typ 3: Behavioral-Das Befehlsdesignmuster

Ein Verhaltensdesignmuster konzentriert sich darauf, wie Klassen und Objekte miteinander kommunizieren. Der Hauptfokus des Befehlsmusters besteht darin, ein höheres Maß an loser Kopplung zwischen beteiligten Parteien einzuprägen (lesen Sie: Klassen).

Uhhhh… Was ist das?

Kopplung ist die Art und Weise, wie zwei (oder mehr) Klassen, die miteinander interagieren, interagieren. Das ideale Szenario, wenn diese Klassen interagieren, ist, dass sie nicht stark voneinander abhängen. Das ist lose Kopplung., Eine bessere Definition für lose Kopplung wäre also, Klassen, die miteinander verbunden sind und sich gegenseitig am wenigsten nutzen.

Die Notwendigkeit für dieses Muster entstand, als Anforderungen gesendet werden mussten, ohne bewusst zu wissen, wonach Sie fragen oder wer der Empfänger ist.

In diesem Muster wird die aufrufende Klasse von der Klasse entkoppelt, die tatsächlich eine Aktion ausführt. Die aufrufende Klasse hat nur die aufrufbare Methode execute, die den erforderlichen Befehl ausführt, wenn der Client ihn anfordert.

Nehmen wir ein einfaches reales Beispiel, das eine Mahlzeit in einem schicken Restaurant bestellt., Im Laufe des Flusses geben Sie Ihre Bestellung (Befehl) an den Kellner (Anrufer) weiter, der sie dann dem Küchenchef(Empfänger) übergibt, damit Sie Essen bekommen können. Klingt vielleicht einfach… aber ein bisschen meh zu codieren.

Die Idee ist ziemlich einfach, aber die Codierung geht um die Nase.,

Command Design Pattern Class Diagram

Der Betriebsfluss auf der technischen Seite besteht darin, dass Sie einen konkreten Befehl ausführen, der die Befehlsschnittstelle implementiert und den Empfänger auffordert, eine Aktion auszuführen, und den Befehl an den Aufrufer senden. Der Aufrufer ist die Person, die weiß, wann dieser Befehl erteilt werden muss. Der Küchenchef ist der einzige, der weiß, was zu tun ist, wenn er den bestimmten Befehl/Auftrag erhält., Wenn also die execute-Methode des Aufrufers ausgeführt wird, führt dies wiederum dazu, dass die execute-Methode der Befehlsobjekte auf dem Empfänger ausgeführt wird, wodurch die erforderlichen Aktionen ausgeführt werden.,

Was wir implementieren müssen, ist;

  1. Ein Schnittstellenbefehl
  2. Eine Klassenreihenfolge, die die Befehlsschnittstelle
  3. Eine Klasse implementiert Kellner (Aufrufer)
  4. Eine Klasse Chef (Empfänger)

Also, die Codierung geht so:

Chef, der Empfänger

Befehl, die Schnittstelle

public interface Command {public abstract void execute();}

Bestellen, der konkrete Befehl

Kellner, der Aufrufer

public class Waiter {private Order order;public Waiter(Order ord) {this.order = ord;}public void execute() {this.order.execute();}}

Sie, der Client

Wie Sie oben sehen können, macht der Client eine Bestellung und legt den Empfänger als Koch fest., Die Bestellung wird an den Kellner gesendet, der weiß, wann er die Bestellung ausführen muss (dh wann er dem Koch die Bestellung zum Kochen geben muss). Wenn der Aufrufer ausgeführt wird, wird die Ausführungsmethode der Aufträge auf dem Empfänger ausgeführt (dh der Chef erhält den Befehl, entweder Pasta zu kochen ? oder Kuchen backen?).,

Final Client Output

Quick recap

In diesem Beitrag haben wir durchgemacht:

  1. Was ein Entwurfsmuster wirklich ist,
  2. Die verschiedenen Arten von Entwurfsmustern und warum sie unterschiedlich sind
  3. Ein grundlegendes oder gemeinsames Entwurfsmuster für jeden Typ

Ich hoffe das war hilfreich.

Hier finden Sie das Code-Repo für den Beitrag.