JavaScript-Entwurfsmuster

Der ultimative Leitfaden für die nützlichsten Designmuster

UPDATE-HINWEIS: Das Proxy-Muster-Beispiel wurde aktualisiert, um ES6 Proxy und Reflect zu verwenden. Ersetzte Bilder von Quellcode-Ausschnitten durch GitHub-Gists.

In diesem Artikel werden wir über Entwurfsmuster sprechen, die verwendet werden können und sollten, um besseren, wartbaren JavaScript-Code zu schreiben. Ich gehe davon aus, dass Sie ein grundlegendes Verständnis von JavaScript und Konzepten wie Klassen (Klassen in JavaScript können schwierig sein), Objekten, prototypischer Vererbung, Closures usw. haben.

Dieser Artikel ist aufgrund der Art des Themas als Ganzes lange gelesen worden, daher habe ich versucht, die Abschnitte in sich geschlossen zu halten. So können Sie als Leser bestimmte Teile (oder in diesem Fall bestimmte Muster) auswählen und ignorieren, die Sie nicht interessieren oder mit denen Sie gut vertraut sind. Jetzt fangen wir an.

Hinweis: Der Quellcode für die Implementierung aller hier erläuterten Entwurfsmuster befindet sich auf GitHub.

Einführung

Wir schreiben Code, um Probleme zu lösen. Diese Probleme weisen normalerweise viele Ähnlichkeiten auf, und wenn wir versuchen, sie zu lösen, stellen wir mehrere gemeinsame Muster fest. Hier kommen Designmuster ins Spiel.

Ein Entwurfsmuster ist ein Begriff, der im Software-Engineering für eine allgemeine, wiederverwendbare Lösung eines häufig auftretenden Problems im Software-Entwurf verwendet wird.

Das zugrunde liegende Konzept von Entwurfsmustern gibt es in der Softwareentwicklungsbranche von Anfang an, aber sie wurden nicht wirklich so formalisiert. Design Patterns: Elemente wiederverwendbarer objektorientierter Software, die von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides - der berühmten Gang of Four (GoF) - geschrieben wurden, waren maßgeblich an der Umsetzung des formalisierten Konzepts von Design Patterns in der Softwareentwicklung beteiligt. Jetzt sind Designmuster ein wesentlicher Bestandteil der Softwareentwicklung und das schon seit langer Zeit.

Das ursprüngliche Buch enthielt 23 Muster.

Die von GoF eingeführten Classic 23 Patterns

Entwurfsmuster sind aus verschiedenen Gründen vorteilhaft. Sie sind bewährte Lösungen, die Branchenveteranen erprobt und getestet haben. Sie sind solide Ansätze, die Probleme auf allgemein anerkannte Weise lösen und die Erfahrung und Erkenntnisse der branchenführenden Entwickler widerspiegeln, die sie definiert haben. Muster verbessern die Wiederverwendbarkeit und Lesbarkeit Ihres Codes und beschleunigen den Entwicklungsprozess erheblich.

Entwurfsmuster sind keineswegs fertige Lösungen. Sie bieten uns nur Ansätze oder Konzepte zur Lösung eines Problems.

Hinweis: In diesem Artikel werden hauptsächlich Entwurfsmuster aus objektorientierter Sicht und im Kontext ihrer Verwendbarkeit in modernem JavaScript behandelt. Aus diesem Grund können viele klassische Muster aus GoF weggelassen werden, und einige moderne Muster aus Quellen wie Addy Osmanis Learn JavaScript Design Patterns werden einbezogen. Die Beispiele sind zum leichteren Verständnis einfach gehalten und daher nicht die optimalste Implementierung ihrer jeweiligen Entwurfsmuster.

Kategorien von Entwurfsmustern

Entwurfsmuster werden normalerweise in drei Hauptgruppen eingeteilt.

Kreative Entwurfsmuster

Wie der Name schon sagt, dienen diese Muster der Handhabung von Objekterstellungsmechanismen. Ein kreatives Entwurfsmuster löst ein Problem im Wesentlichen, indem der Erstellungsprozess eines Objekts gesteuert wird.

Wir werden die folgenden Muster im Detail behandeln: Konstruktormuster, Factory-Muster, Prototypmuster und Singleton-Muster.

Strukturelle Entwurfsmuster

Diese Muster befassen sich mit der Klassen- und Objektzusammensetzung. Sie helfen bei der Strukturierung oder Umstrukturierung eines oder mehrerer Teile, ohne das gesamte System zu beeinträchtigen. Mit anderen Worten, sie helfen, neue Funktionen zu erhalten, ohne die vorhandenen zu verändern.

Wir werden die folgenden Muster im Detail besprechen: Adaptermuster, Verbundmuster, Dekorationsmuster, Fassadenmuster, Fliegengewichtmuster und Proxy-Muster.

Verhaltensmuster

Diese Muster befassen sich mit der Verbesserung der Kommunikation zwischen verschiedenen Objekten.

Wir werden die folgenden Muster im Detail diskutieren: Muster der Verantwortungskette, Befehlsmuster, Iteratormuster, Mediatormuster, Beobachtermuster, Statusmuster, Strategiemuster und Vorlagenmuster.

Konstruktormuster

Dies ist ein klassenbasiertes Gestaltungsmuster. Konstruktoren sind spezielle Funktionen, mit denen neue Objekte mit von dieser Funktion definierten Methoden und Eigenschaften instanziiert werden können.

Es ist nicht eines der klassischen Designmuster. Tatsächlich ist es in den meisten objektorientierten Sprachen eher ein grundlegendes Sprachkonstrukt als ein Muster. In JavaScript können Objekte jedoch im Handumdrehen ohne Konstruktorfunktionen oder Klassendefinition erstellt werden. Daher halte ich es für wichtig, die Grundlage für andere Muster zu legen, die mit diesem einfachen Muster einhergehen.

Konstruktormuster sind eines der am häufigsten verwendeten Muster in JavaScript zum Erstellen neuer Objekte einer bestimmten Art.

In diesem Beispiel definieren wir eine Hero-Klasse mit Attributen wie name und specialAbility und Methoden wie getDetails. Anschließend instanziieren wir ein Objekt IronMan, indem wir die Konstruktormethode mit dem neuen Schlüsselwort aufrufen und die Werte für die jeweiligen Attribute als Argumente übergeben.

Fabrik-Muster

Factory-Pattern ist ein weiteres klassenbasiertes Schöpfungsmuster. In diesem Abschnitt stellen wir eine generische Schnittstelle bereit, die die Verantwortung für die Objektinstanziierung an ihre Unterklassen delegiert.

Dieses Muster wird häufig verwendet, wenn wir Sammlungen von Objekten verwalten oder bearbeiten müssen, die unterschiedlich sind, aber viele ähnliche Merkmale aufweisen.

In diesem Beispiel erstellen wir eine Factory-Klasse mit dem Namen BallFactory, die über eine Methode verfügt, die Parameter akzeptiert, und die abhängig von den Parametern die Objektinstanziierungsverantwortung an die jeweilige Klasse delegiert. Wenn der Typparameter "football" oder "soccer" ist, wird die Objektinstanziierung von der Football-Klasse behandelt, wenn es sich jedoch um "basketball" handelt, wird die Objektinstanziierung von der Basketball-Klasse behandelt.

Muster des Prototyps

Dieses Muster ist ein objektbasiertes kreatives Entwurfsmuster. In diesem Beispiel verwenden wir eine Art „Skelett“ eines vorhandenen Objekts, um neue Objekte zu erstellen oder zu instanziieren.

Dieses Muster ist für JavaScript besonders wichtig und vorteilhaft, da es anstelle einer klassischen objektorientierten Vererbung eine prototypische Vererbung verwendet. Daher spielt es die Stärke von JavaScript aus und wird nativ unterstützt.

In diesem Beispiel haben wir ein Autoobjekt, das wir als Prototyp verwenden, um ein anderes Objekt zu erstellen. MyCar mit der Object.create-Funktion von JavaScript und um einen zusätzlichen Eigentümer für das neue Objekt zu definieren.

Singleton-Muster

Singleton ist ein spezielles Entwurfsmuster, in dem nur eine Instanz einer Klasse existieren kann. Dies funktioniert folgendermaßen: Wenn keine Instanz der Singleton-Klasse vorhanden ist, wird eine neue Instanz erstellt und zurückgegeben. Wenn jedoch bereits eine Instanz vorhanden ist, wird der Verweis auf die vorhandene Instanz zurückgegeben.

Ein perfektes Beispiel für das wirkliche Leben wäre Mungo (die berühmte Node.js ODM-Bibliothek für MongoDB). Es wird das Singleton-Muster verwendet.

In diesem Beispiel haben wir eine Database-Klasse, die ein Singleton ist. Zuerst erstellen wir ein Objekt mongo, indem wir den new-Operator verwenden, um den Database-Klassenkonstruktor aufzurufen. Diesmal wird ein Objekt instanziiert, da keines bereits vorhanden ist. Wenn wir das mysql-Objekt zum zweiten Mal erstellen, wird kein neues Objekt instanziiert, sondern der Verweis auf das zuvor instanziierte Objekt, d. H. Das Mongo-Objekt, wird zurückgegeben.

Adaptermuster

Dies ist ein Strukturmuster, bei dem die Schnittstelle einer Klasse in eine andere übersetzt wird. Mit diesem Muster können Klassen zusammenarbeiten, die aufgrund inkompatibler Schnittstellen nicht anders könnten.

Dieses Muster wird häufig verwendet, um Wrapper für neu überarbeitete APIs zu erstellen, damit andere vorhandene alte APIs weiterhin mit ihnen arbeiten können. Dies erfolgt normalerweise, wenn neue Implementierungen oder Code-Refactoring (z. B. aus Gründen der Leistungssteigerung) zu einer anderen öffentlichen API führen, während die anderen Teile des Systems weiterhin die alte API verwenden und für die Zusammenarbeit angepasst werden müssen.

In diesem Beispiel haben wir eine alte API, d. H. Die OldCalculator-Klasse, und eine neue API, d. H. Die NewCalculator-Klasse. Die OldCalculator-Klasse bietet eine Operationsmethode für Addition und Subtraktion, während der NewCalculator separate Methoden für Addition und Subtraktion bereitstellt. Die Adapterklasse CalcAdapter umschließt den NewCalculator, um die Operationsmethode der öffentlich zugänglichen API hinzuzufügen, während eine eigene Additions- und Subtraktionsimplementierung unter der Haube verwendet wird.

Zusammengesetztes Muster

Dies ist ein strukturelles Entwurfsmuster, das Objekte zu baumartigen Strukturen zusammensetzt, um ganzheitliche Teilehierarchien darzustellen. In diesem Muster kann jeder Knoten in der baumartigen Struktur entweder ein einzelnes Objekt oder eine zusammengesetzte Sammlung von Objekten sein. Unabhängig davon wird jeder Knoten einheitlich behandelt.

Eine mehrstufige Menüstruktur

Die Visualisierung dieses Musters ist etwas komplex. Der einfachste Weg, darüber nachzudenken, ist das Beispiel eines mehrstufigen Menüs. Jeder Knoten kann eine eigene Option sein, oder es kann sich um ein Menü handeln, dessen untergeordnetes Element mehrere Optionen aufweist. Eine Knotenkomponente mit untergeordneten Elementen ist eine zusammengesetzte Komponente, während eine Knotenkomponente ohne untergeordnetes Element eine Blattkomponente ist.

In diesem Beispiel erstellen wir eine Basisklasse von Component, die die erforderlichen allgemeinen Funktionen implementiert und die anderen erforderlichen Methoden abstrahiert. Die Basisklasse verfügt auch über eine statische Methode, die mithilfe der Rekursion eine zusammengesetzte Baumstruktur durchläuft, die mit ihren Unterklassen erstellt wurde. Anschließend erstellen wir zwei Unterklassen, die die Basisklasse erweitern: Leaf ohne untergeordnete Klassen und Composite mit untergeordneten Klassen. Daher verfügen wir über Methoden zum Hinzufügen, Suchen und Entfernen von untergeordneten Funktionen. Die beiden Unterklassen werden verwendet, um eine zusammengesetzte Struktur zu erstellen - in diesem Fall einen Baum.

Dekorateur Muster

Dies ist auch ein strukturelles Entwurfsmuster, das sich auf die Fähigkeit konzentriert, vorhandenen Klassen Verhalten oder Funktionen dynamisch hinzuzufügen. Es ist eine weitere Alternative zur Unterklassifizierung.

Das Verhalten des Dekoratortyps ist in JavaScript sehr einfach zu implementieren, da wir mit JavaScript dynamisch Methoden und Eigenschaften zum Objekt hinzufügen können. Der einfachste Ansatz wäre, einem Objekt lediglich eine Eigenschaft hinzuzufügen, die jedoch nicht effizient wiederverwendet werden kann.

Tatsächlich gibt es einen Vorschlag, der JavaScript-Sprache Dekoratoren hinzuzufügen. Schauen Sie sich Addy Osmanis Beitrag über Dekorateure in JavaScript an.

Wenn Sie mehr über den Vorschlag selbst erfahren möchten, wenden Sie sich an uns.

In diesem Beispiel erstellen wir eine Book-Klasse. Wir erstellen außerdem zwei Dekorationsfunktionen, die ein Buchobjekt akzeptieren und ein "dekoriertes" Buchobjekt zurückgeben - giftWrap, das ein neues Attribut und eine neue Funktion hinzufügt, und hardbindBook, das ein neues Attribut hinzufügt und den Wert eines vorhandenen Attributs bearbeitet.

Fassadenmuster

Dies ist ein strukturelles Entwurfsmuster, das in den JavaScript-Bibliotheken häufig verwendet wird. Es wird verwendet, um eine vereinheitlichte und einfachere öffentlich zugängliche Benutzeroberfläche bereitzustellen, die die Komplexität der vorhandenen Subsysteme oder Subklassen abschirmt.

Die Verwendung dieses Musters ist in Bibliotheken wie jQuery weit verbreitet.

In diesem Beispiel erstellen wir eine öffentlich zugängliche API mit der Klasse ComplaintRegistry. Es wird nur eine Methode verfügbar gemacht, die vom Client verwendet werden soll, d. H. RegisterComplaint. Die Instanziierung erforderlicher Objekte von ProductComplaint oder ServiceComplaint wird basierend auf dem Typargument intern verarbeitet. Es behandelt auch alle anderen komplexen Funktionen wie das Generieren einer eindeutigen ID, das Speichern der Beschwerde im Speicher usw. All diese Komplexitäten werden jedoch mithilfe des Fassadenmusters verborgen.

Fliegengewicht Muster

Dies ist ein strukturelles Entwurfsmuster, das sich auf die effiziente gemeinsame Nutzung von Daten durch feinkörnige Objekte konzentriert. Es wird für Effizienz- und Speichererhaltungszwecke verwendet.

Dieses Muster kann für beliebige Caching-Zwecke verwendet werden. Tatsächlich verwenden moderne Browser eine Variante eines Flyweight-Musters, um zu verhindern, dass dieselben Bilder zweimal geladen werden.

In diesem Beispiel erstellen wir eine feinkörnige Fliegengewichtsklasse Icecream, um Daten zu Eisgeschmacksrichtungen auszutauschen, und eine Factory-Klasse IcecreamFactory, um diese Fliegengewichtsobjekte zu erstellen. Zur Erhaltung des Arbeitsspeichers werden die Objekte wiederverwendet, wenn dasselbe Objekt zweimal instanziiert wird. Dies ist ein einfaches Beispiel für die Implementierung von Fliegengewicht.

Proxy-Muster

Dies ist ein strukturelles Entwurfsmuster, das sich genau so verhält, wie es der Name vermuten lässt. Es fungiert als Ersatz oder Platzhalter für ein anderes Objekt, um den Zugriff darauf zu steuern.

Es wird normalerweise in Situationen verwendet, in denen ein Zielobjekt Einschränkungen unterliegt und möglicherweise nicht in der Lage ist, alle seine Verantwortlichkeiten effizient zu erfüllen. In diesem Fall stellt ein Proxy dem Client in der Regel dieselbe Schnittstelle zur Verfügung und fügt eine Indirektionsebene hinzu, um den kontrollierten Zugriff auf das Zielobjekt zu unterstützen und unnötigen Druck darauf zu vermeiden.

Das Proxy-Muster kann bei der Arbeit mit Anwendungen mit hohen Netzwerkanforderungen sehr nützlich sein, um unnötige oder redundante Netzwerkanforderungen zu vermeiden.

In diesem Beispiel verwenden wir zwei neue ES6-Funktionen, Proxy und Reflect. Ein Proxy-Objekt wird verwendet, um ein benutzerdefiniertes Verhalten für grundlegende Operationen eines JavaScript-Objekts zu definieren (Merken, Funktion und Arrays sind auch Objekte in JavaScript). Es ist eine Konstruktormethode, mit der ein Proxy-Objekt erstellt werden kann. Es akzeptiert ein Zielobjekt, das als Proxy verwendet werden soll, und ein Handlerobjekt, das die erforderliche Anpassung definiert. Mit dem Handler-Objekt können einige Trap-Funktionen wie get, set, has, apply usw. definiert werden, die zum Hinzufügen eines benutzerdefinierten Verhaltens verwendet werden, das mit ihrer Verwendung verknüpft ist. Reflect hingegen ist ein integriertes Objekt, das ähnliche Methoden bereitstellt, die vom Handler-Objekt von Proxy als statische Methoden für sich selbst unterstützt werden. Es ist kein Konstruktor; Die statischen Methoden werden für abfangfähige JavaScript-Vorgänge verwendet.

Jetzt erstellen wir eine Funktion, die als Netzwerkanforderung betrachtet werden kann. Wir haben es als networkFetch bezeichnet. Es akzeptiert eine URL und reagiert entsprechend. Wir möchten einen Proxy implementieren, bei dem wir die Antwort nur vom Netzwerk erhalten, wenn sie nicht in unserem Cache verfügbar ist. Andernfalls geben wir nur eine Antwort aus dem Cache zurück.

Die globale Cache-Variable speichert unsere zwischengespeicherten Antworten. Wir erstellen einen Proxy namens proxiedNetworkFetch mit unserem ursprünglichen networkFetch als Ziel und verwenden die apply-Methode in unserem Handler-Objekt, um den Funktionsaufruf als Proxy zu verwenden. Die Methode apply wird an das Zielobjekt selbst übergeben. Dieser Wert als thisArg und die Argumente werden in Array-ähnlichen Strukturargumenten an ihn übergeben.

Wir prüfen, ob sich das übergebene URL-Argument im Cache befindet. Wenn es im Cache vorhanden ist, geben wir die Antwort von dort zurück und rufen niemals die ursprüngliche Zielfunktion auf. Wenn dies nicht der Fall ist, verwenden wir die Reflect.apply-Methode, um die target-Funktion mit thisArg und den übergebenen Argumenten aufzurufen (obwohl dies in unserem Fall keine Bedeutung hat).

Chain of Responsibility Pattern

Dies ist ein Verhaltensentwurfsmuster, das eine Kette lose gekoppelter Objekte bereitstellt. Jedes dieser Objekte kann auswählen, ob auf die Anforderung des Clients reagiert oder diese verarbeitet werden soll.

Ein gutes Beispiel für das Muster der Verantwortungskette ist das Ereignis, das im DOM sprudelt und in dem sich ein Ereignis über eine Reihe verschachtelter DOM-Elemente ausbreitet, an die möglicherweise ein „Ereignis-Listener“ angehängt ist, um das Ereignis abzuhören und darauf zu reagieren.

In diesem Beispiel erstellen wir eine Klasse CumulativeSum, die mit einem optionalen initialValue instanziiert werden kann. Es verfügt über eine Methode add, die den übergebenen Wert zum Summenattribut des Objekts hinzufügt und das Objekt selbst zurückgibt, um die Verkettung von Methodenaufrufen add zu ermöglichen.

Dies ist ein verbreitetes Muster, das auch in jQuery zu sehen ist, wo fast jeder Methodenaufruf für ein jQuery-Objekt ein jQuery-Objekt zurückgibt, sodass Methodenaufrufe miteinander verkettet werden können.

Befehlsmuster

Dies ist ein Verhaltensentwurfsmuster, das darauf abzielt, Aktionen oder Operationen als Objekte zu kapseln. Dieses Muster ermöglicht die lose Kopplung von Systemen und Klassen, indem die Objekte, die eine Operation anfordern oder eine Methode aufrufen, von den Objekten getrennt werden, die die eigentliche Implementierung ausführen oder verarbeiten.

Die Interaktions-API für die Zwischenablage ähnelt etwas dem Befehlsmuster. Wenn Sie ein Redux-Benutzer sind, sind Sie bereits auf das Befehlsmuster gestoßen. Die Aktionen, die das großartige Zeitreise-Debugging ermöglichen, sind nichts anderes als gekapselte Vorgänge, die zum Wiederherstellen oder Rückgängigmachen von Vorgängen nachverfolgt werden können. Zeitreisen sind also möglich.

In diesem Beispiel haben wir eine Klasse namens SpecialMath mit mehreren Methoden und eine Command-Klasse, die Befehle kapselt, die für ihr Subjekt ausgeführt werden sollen, d. H. Ein Objekt der SpecialMath-Klasse. Die Command-Klasse protokolliert auch alle ausgeführten Befehle, mit denen die Funktionalität um Operationen vom Typ "Rückgängig" und "Wiederherstellen" erweitert werden kann.

Iteratormuster

Hierbei handelt es sich um ein Verhaltensentwurfsmuster, mit dem auf die Elemente eines aggregierten Objekts nacheinander zugegriffen werden kann, ohne dass die zugrunde liegende Darstellung verfügbar gemacht wird.

Iteratoren haben ein besonderes Verhalten, bei dem wir nacheinander einen geordneten Satz von Werten durchlaufen, indem wir next () aufrufen, bis wir das Ende erreichen. Die Einführung von Iterator und Generatoren in ES6 machte die Implementierung des Iteratormusters äußerst einfach.

Wir haben zwei Beispiele unten. Erstens verwendet eine IteratorClass die Iterator-Spezifikation, während die andere IteratorUsingGenerator Generatorfunktionen verwendet.

Der Symbol.iterator (Symbol - eine neue Art von primitivem Datentyp) wird verwendet, um den Standarditerator für ein Objekt anzugeben. Es muss definiert sein, damit eine Auflistung das Konstrukt for ... of looping verwenden kann. Im ersten Beispiel definieren wir den Konstruktor zum Speichern einiger Datensammlungen und definieren dann Symbol.iterator, der ein Objekt mit der nächsten Methode für die Iteration zurückgibt.

Für den zweiten Fall definieren wir eine Generatorfunktion, die ein Array von Daten übergibt und deren Elemente iterativ mit next und yield zurückgibt. Eine Generatorfunktion ist eine spezielle Art von Funktion, die als Factory für Iteratoren fungiert und ihren eigenen internen Zustand explizit beibehalten und iterativ Werte liefern kann. Es kann seinen eigenen Ausführungszyklus anhalten und wieder aufnehmen.

Mediator-Muster

Es ist ein Verhaltensentwurfsmuster, das die Interaktion einer Gruppe von Objekten miteinander einschließt. Es bietet die zentrale Autorität über eine Gruppe von Objekten, indem es die lose Kopplung fördert und verhindert, dass sich Objekte explizit aufeinander beziehen.

In diesem Beispiel haben wir TrafficTower als Vermittler, der die Art und Weise steuert, wie Flugzeugobjekte miteinander interagieren. Alle Airplane-Objekte registrieren sich selbst bei einem TrafficTower-Objekt, und es ist das Mediator-Klassenobjekt, das behandelt, wie ein Airplane-Objekt Koordinatendaten aller anderen Airplane-Objekte empfängt.

Beobachter-Muster

Es ist ein entscheidendes Verhaltensentwurfsmuster, das Eins-zu-Viele-Abhängigkeiten zwischen Objekten definiert, sodass alle anderen abhängigen Objekte (Abonnenten) automatisch benachrichtigt und aktualisiert werden, wenn ein Objekt (Herausgeber) seinen Status ändert. Dies wird auch als PubSub (Publisher / Abonnenten) oder Event-Dispatcher / Listener-Muster bezeichnet. Der Verlag wird manchmal als Thema bezeichnet, und die Abonnenten werden manchmal als Beobachter bezeichnet.

Möglicherweise sind Sie mit diesem Muster bereits einigermaßen vertraut, wenn Sie addEventListener oder jQuery zum Schreiben von Code für eine gleichmäßige Verarbeitung verwendet haben. Es hat auch seine Einflüsse in der reaktiven Programmierung (denken Sie an RxJS).

In diesem Beispiel erstellen wir eine einfache Subject-Klasse mit Methoden zum Hinzufügen und Entfernen von Objekten der Observer-Klasse aus der Subscriber Collection. Außerdem eine Feuermethode zum Übertragen von Änderungen im Subject-Klassenobjekt an die abonnierten Observer. Die Observer-Klasse hingegen verfügt über einen internen Status und eine Methode zum Aktualisieren des internen Status basierend auf der Änderung, die von dem von ihr abonnierten Betreff übermittelt wurde.

Zustandsmuster

Es ist ein Verhaltensentwurfsmuster, mit dem ein Objekt sein Verhalten basierend auf Änderungen seines internen Zustands ändern kann. Das von einer Statusmusterklasse zurückgegebene Objekt scheint seine Klasse zu ändern. Es bietet zustandsspezifische Logik für eine begrenzte Menge von Objekten, in denen jeder Objekttyp einen bestimmten Zustand darstellt.

Wir nehmen ein einfaches Beispiel einer Ampel, um dieses Muster zu verstehen. Die TrafficLight-Klasse ändert das Objekt, das sie zurückgibt, basierend auf ihrem internen Status, der ein Objekt der roten, gelben oder grünen Klasse ist.

Strategie-Muster

Es ist ein Verhaltensentwurfsmuster, das die Kapselung alternativer Algorithmen für eine bestimmte Aufgabe ermöglicht. Es definiert eine Familie von Algorithmen und kapselt sie so, dass sie zur Laufzeit ohne Einmischung oder Wissen des Clients austauschbar sind.

Im folgenden Beispiel erstellen wir eine Klasse Commute, um alle möglichen Strategien für den Pendelverkehr zur Arbeit zu kapseln. Dann definieren wir drei Strategien, nämlich Bus, PersonalCar und Taxi. Mit diesem Muster können wir die Implementierung zur Laufzeit gegen die Reisemethode des Commute-Objekts austauschen.

Schablonenmuster

Dies ist ein Verhaltensentwurfsmuster, das auf der Definition des Gerüsts des Algorithmus oder der Implementierung einer Operation basiert, jedoch einige Schritte auf Unterklassen verschiebt. Hiermit können Unterklassen bestimmte Schritte eines Algorithmus neu definieren, ohne die äußere Struktur des Algorithmus zu ändern.

In diesem Beispiel haben wir eine Template-Klasse Employee, die die Arbeitsmethode teilweise implementiert. Die Unterklassen müssen die Verantwortlichkeitsmethode implementieren, damit sie als Ganzes funktioniert. Anschließend erstellen wir zwei Unterklassen Developer und Tester, die die Vorlagenklasse erweitern und die erforderliche Methode implementieren, um die Implementierungslücke zu schließen.

Fazit

Entwurfsmuster sind für die Softwareentwicklung von entscheidender Bedeutung und können bei der Lösung allgemeiner Probleme sehr hilfreich sein. Aber das ist ein sehr umfangreiches Thema, und es ist einfach nicht möglich, alles in einem kurzen Stück zusammenzufassen. Aus diesem Grund habe ich mich entschlossen, kurz und prägnant nur über diejenigen zu sprechen, von denen ich denke, dass sie beim Schreiben von modernem JavaScript sehr nützlich sind. Um tiefer einzutauchen, schlage ich vor, dass Sie sich diese Bücher ansehen:

  1. Entwurfsmuster: Elemente wiederverwendbarer objektorientierter Software von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides (Viererbande)
  2. Erfahren Sie JavaScript Design Patterns von Addy Osmani
  3. JavaScript-Muster von Stoyan Stefanov