Ein wenig über generative Designmuster

Das Thema Designmuster ist sehr beliebt. Es wurden viele Videos gedreht und Artikel geschrieben. Kombiniert all diese Materialien mit einem "Anti-Muster". Versehentliche Komplexität. Infolgedessen sind die Beispiele abstrus, die Beschreibung ist verwirrend, die Anwendung ist nicht klar. Und die Hauptaufgabe von Entwurfsmustern - Vereinfachung (von Code und Arbeit im Allgemeinen) wird nicht erreicht. Schließlich erfordert die Verwendung der Vorlage zusätzlichen Aufwand. Ungefähr das Gleiche wie Unit-Tests.


Ich werde versuchen, die Entwurfsmuster dahingehend zu erklären, wie sie wo und warum angewendet werden.


Sechs Generatoren können zugeordnet werden:


  • Prototyp
  • Abstrakte Fabrik,
  • Fabrikmethode
  • Baumeister
  • Singleton
  • Faule Initialisierung.

Alle anderen Muster, die sich auf Generatoren beziehen, sind ein Sonderfall der Anwendung, und es macht keinen Sinn, auf sie einzugehen.


Das Generieren von Mustern kann in drei Gruppen unterteilt werden, auf deren Frage sie antworten. Also drei Fragen:


  • Wo?
  • Wie?
  • Wann?

Wo?


Drei Muster beantworten diese Frage: Prototyp, abstrakte Fabrik und Fabrikmethode.


Ein bisschen über die Begriffe

Im Rahmen des OOP-Konzepts gibt es nur drei Stellen, an denen theoretisch eine neue Instanz generiert werden kann.


  • Produkt ist die Klasse, die instanziiert wird.
  • Client ist die Klasse, die die instanziierte Instanz verwendet.
  • Partner - jede dritte Klasse im Bereich der Sichtbarkeit des Kunden.


Tatsächlich bestimmen diese Muster den Ort der Erzeugung. Darüber hinaus sind sie hierarchisch miteinander verbunden und haben einen anderen Umfang. Die Verbindung ist in der Abbildung dargestellt, die Pfeile bestimmen die Richtung der Anrufe.


Die Hierarchie generativer Muster

Bei der Implementierung kann die „Factory-Methode“ die Generierung einer Instanz entweder an die vorhandene „Factory“ oder an den „Prototype“ delegieren. Der „Prototyp“ sollte jedoch von niemandem abhängig sein und alles selbst machen. Jetzt genauer.


"Prototyp"


Diese Vorlage entspricht dem Ort "Produkt", der tatsächlich der Konstruktor der Klasse ist. Dementsprechend wird immer eine Instanz einer bestimmten (zuvor bekannten) Klasse generiert.
Im Rahmen dieser Vorlage kennt der Designer nur die direkt an ihn übergebenen Parameter (die Anzahl der Parameter richtet sich nach der Anzahl der Klassenfelder). Natürlich gibt es vollen Zugriff auf alle Felder und Eigenschaften der erstellten Klasse.


Richtig implementierte Methoden des "Prototyps" ermöglichen es Ihnen, zusätzliche Initialisierungsmethoden in der Öffentlichkeit loszuwerden. Die externe Schnittstelle der Klasse wird wiederum einfacher und weniger verlockend, die Klasse für andere Zwecke zu verwenden.


Was diese Vorlage uns gibt:


  • Geringe Konnektivität - Die Klasse kennt sich nur selbst und ist nicht von externen Daten abhängig.
  • Erweiterbarkeit - Konstruktoren können neu definiert oder Nachkommen hinzugefügt werden.

Von den Minuspunkten:


  • Bei komplexen Klassen müssen Sie möglicherweise viele Parameter für die Initialisierung übergeben. Obwohl es eine triviale Lösung gibt.
  • Die direkte Verwendung im Client kann die Lesbarkeit beeinträchtigen und praktisch die Möglichkeit blockieren, den Typ der Instanz zu überschreiben, die im Nachkommen des Clients generiert wird.

Die beliebteste Vorlage. Jeder benutzt es, aber nur wenige wissen, was es benutzt. Es ist gut, bis das erste Arbeitsmodell erhalten ist, bis Klassen und ihre Beziehungen vollständig definiert sind. Danach sind die Verarbeitung und die Erhöhung der Abstraktion obligatorisch.


"Abstrakte Fabrik"


Einige Klassenpartner. Es kann spezialisiert oder "kombiniert" werden. Kann statisch sein (keine Instanz). Ein Beispiel für das „Kombinieren“ kann eine Konfigurationsklasse sein. Es kann auch hinter der Fassade versteckt sein.


"Factory" sieht normalerweise alle globalen Einstellungen der Anwendung (oder eines separaten Subsystems). Die sofortige Generierung kann an den Prototyp delegiert werden. Gleichzeitig ist die Anzahl der Eingabeparameter in der Factory-Methode geringer als in einem ähnlichen Prototype-Konstruktor. Die Fabrik entscheidet nicht anhand der eingehenden Parameter, wer erstellt werden soll.


Diese Vorlage ist sehr praktisch und einfach zu implementieren, erfordert jedoch ein vorläufiges Design. Wenn Sie für alles Fabriken erstellen, wird der Code dadurch komplizierter. Tatsächlich erhalten wir ein Analogon des Prototyps, sind jedoch in eine Klasse von Drittanbietern umgezogen.


Von den Profis:


  • Gute Neudefinition bei Nachkommen
  • Vereinfachter Anruf
  • Auf Basis der Factory ist es einfach, eine Substitution zu implementieren (State Template)

Es gibt aber auch Nachteile:



  • Es erfordert Design, insbesondere für universelle Fabriken (die in vielen Projekten verwendet werden). Mit anderen Worten, es ist nicht einfach, sofort eine gute Fabrik zu bekommen.
  • Es ist sehr einfach, den Code durcheinander zu bringen. Es gibt zwei Hauptbereiche:
    • Rutschen in Richtung Prototyp, aber in einer externen Klasse. Methoden sind mit Parametern überladen, es gibt viele Methoden selbst. Infolgedessen ist die Vererbung sowohl in der Factory selbst als auch im Client schwierig.
    • Fabrik mit universeller Methode. Diese Methode gibt abhängig von den übergebenen Parametern eine beliebige Instanz zurück. Das Ergebnis wie im ersten Fall.


Sehr beliebt. Diese Vorlage wird von denjenigen verwendet, die am GoF-Kurs teilgenommen haben. In der Regel wird der Code noch schlechter als "vor dem Anwenden der Vorlagen".


Es ist sinnvoll, wenn Fabriken während der ersten Code-Überarbeitung angezeigt werden. Zu diesem Zeitpunkt sind bereits Kombinationen von Parametern für die erstellten Instanzen bekannt, und es wird nicht schwierig sein, verallgemeinerte Factory-Methoden zu schreiben. Dadurch werden Anrufe im Client vereinfacht.


In einigen Fällen ist es zweckmäßig, Fabriken hinter der Fassade zu verstecken. Beispielsweise verfügt die Anwendung über ein Dutzend Fabriken und ein Dutzend Bibliotheken. Für sie kann man eine Fassade bauen. Dadurch können Bibliotheken nicht mit jedem Modul verknüpft werden, und es ist auch einfach, eine Fabrik durch eine andere zu ersetzen.


Fabrikmethode


Die Spitze der Abstraktion in generativen Mustern. Herkunftsort Kunde. Die Klasse, in die jedes Produkt in die Fabrikmethode eingestuft wird, hat jede Chance auf ein langes Leben. Ohne Fanatismus muss die angenommene Entwicklungsachse unbedingt auf dieser Vorlage basieren.


Die Factory-Methode sieht nicht über ihre Klasse hinaus. Die Anzahl der direkt übertragenen Parameter sollte minimal sein (im Grenzbereich von Null). Die Methode selbst muss unter Berücksichtigung der Möglichkeit einer Überlappung im Nachkommen erstellt werden.


Ein häufiger Fehler ist die komplizierte Initialisierung in einer Methode. Wenn Sie beispielsweise eine komplexe Instanz (die Builder-Vorlage) erstellen, werden alle Teile des zukünftigen Objekts in einer Methode erstellt. Infolgedessen ist es schwierig, ein solches Verfahren im Nachkommen zu überlappen.


Von den Profis:


  • Es ist einfach, die Vorlagenvorlagenmethode anzupassen
  • Wir erhalten prägnanten Code, in dem die Logik klar sichtbar ist (sie muss nicht unter einem Haufen von Methoden und Parametern betrachtet werden).

Es gibt im Wesentlichen keine Nachteile.


Diese Vorlage wird fast nie verwendet. In der Regel ist dies nur in Projekten mit tiefgreifender Vorarbeit zu sehen. Ideal, wenn die Factory-Methode die Generierung an "Factory" oder "Prototype" delegiert.


Kleines Beispiel


Wir haben eine Klasse zum Protokollieren in einer Datei auf der Festplatte. So könnten generische Methoden in den "Wo?" - Mustern aussehen:


Prototyp:


constructor Create(aFilename: string; aLogLevel: TLogLevel); 

Alles, was der Designer wissen sollte, wird ihm in Form von Parametern übergeben.


Fabrik:


 function GetLogger(aLogLevel: TLogLevel): ILogger; 

Die Fabrik weiß, in welche Datei geschrieben werden soll, wie in den Anwendungseinstellungen angegeben.


Fabrikmethode:


 function NewLogger: ILogger; 

In der Client-Klasse ist bekannt, mit welchen Details protokolliert werden soll.


In diesem Entwurf reicht es aus, NewLogger im Nachkommen des Clients neu zu definieren, um die Protokollierungsklasse durch einen Stub zu ersetzen. Dies ist nützlich, wenn Unit-Tests durchgeführt werden.


Um sich bei der Datenbank anzumelden, reicht es aus, die GetLogger-Methode im Nachkommen der Factory zu überschreiben.

Source: https://habr.com/ru/post/de467817/


All Articles