Es gibt ein Problem bei der Beschreibung und Interpretation der Entwicklungsprinzipien der SOLID-Architektur (Urheberschaft von Robert Martin). Viele Quellen geben ihre Definition und sogar Beispiele für ihre Verwendung an. Als ich sie studierte und versuchte, mich selbst zu benutzen, fing ich ständig an zu denken, dass es nicht genug Erklärungen für die Magie ihrer Anwendung gab. Und zu versuchen, die inneren Zahnräder zu sehen, zu verstehen - und für mich bedeutet es, sich zu erinnern -, sie in ihren "Begriffen-Regalen" auszulegen. Nun, wenn es jemand anderem nützlich sein wird.

Wir fahren fort, die Regale des obigen Entwurfsansatzes zu "jonglieren".
Prinzip der alleinigen Verantwortung (Single Responsibility Principle, SRP)
Ein Code sollte sich nur während der Implementierung eines Ziels ändern. Wenn ein Codeabschnitt zwei Aufgaben und Änderungen für unterschiedliche Zwecke implementiert, sollten Sie diesen Abschnitt für jeden Zweck in einer Instanz duplizieren. Dies ist sehr wichtig, da hiervon vom allgemein anerkannten Prinzip der Beseitigung von Doppelarbeit abgewichen werden muss.
Der Zweck dieses Prinzips besteht darin, implizite Fehler zu beseitigen, die aufgrund der Tatsache entstehen, dass die folgenden Invarianten für die Entwicklung eines Codeabschnitts, einer Prozedur, einer Klasse, einer Komponente existieren (im Folgenden wird der Begriff [Komponente] verwendet, um diese Konzepte zu kombinieren):
- [1] korrekt geschriebene [Komponente] wird notwendigerweise und häufiger mehrmals verwendet,
- [2] Von [Komponente] wird erwartet, dass sie an jedem Einsatzort ein konsistentes Verhalten beibehält, das zu einem wiederholbaren Ergebnis führt.
- [3] Wenn Sie die [Komponente] an mehreren Stellen verwenden, sollte das Ergebnis jedem Verwendungsort entsprechen.
- Wenn eine Änderung in [Komponente] für einen der Verwendungsorte erforderlich ist und das vorherige Verhalten von [Komponente] für einen anderen Verwendungsort erforderlich ist, muss eine Kopie von [Komponente] erstellt und anschließend geändert werden (oder [Komponente] mit zusätzlichen Parametern verallgemeinert werden, die ein anderes Verhalten bieten).
Wenn es Verwendungsorte der [Komponente] gibt, die für die vom Programmierer gelöste aktuelle Aufgabe nicht wichtig sind, kann er leicht vergessen, die Kompatibilität mit diesen Verwendungsorten der an dieser [Komponente] vorgenommenen Änderungen zu überprüfen.
Daher sollten sich alle Verwendungsorte in der Zone [Einzelverantwortung] einer Einzelverantwortung befinden, dh bei Problemen, die vom Programmierer gelöst werden, sofort geändert und berücksichtigt werden.
Das Prinzip gilt sowohl für einen Codeabschnitt als auch für eine Komponente, eine Bibliothek, ein Programm oder eine Reihe von Programmen, die an mehreren Stellen verwendet werden.
Viele Quellen geben ein Beispiel für eine Klasse mit nur einer "Funktion" als Ideal von SRP und der Klasse des "göttlichen Objekts", die alle Funktionen der Anwendung kombiniert, als Antimuster. Eine IMHO-Klasse mit nur einer „Funktion“ ist eine Voraussetzung für eine vorzeitige Optimierung der Codearchitektur, die dazu führt, dass viele Klassen (Code-Entitäten) von Grund auf neu geschrieben werden. Dabei wird vergessen, dass der Programmierer durch das Fehlen von mehr als einem Verwendungsort schnell eine kleine Menge lokal (in einer Klasse) auswerten kann. Interaktion von Code, als die Außenbeziehungen unterschiedlicher Code-Entitäten zu analysieren, die für ihre "Funktion" verantwortlich sind. Ein „göttliches Objekt“ für eine winzige Anwendung ist ebenfalls kein starkes Verbrechen - es ermöglicht Ihnen, mit der Entwicklung zu beginnen: Wählen Sie alle erforderlichen Entitäten aus und schreiben Sie sie nebeneinander, getrennt von den externen Objekten der Standardbibliothek und externen Modulen (erstellen Sie eine lebende Zelle und trennen Sie sie mit einer Membran). Während des Wachstums und der Entwicklung des Projekts gibt es viele Methoden, die helfen, der SRP zu folgen. Eine davon ist die Einteilung in Klassen und die Minimierung der Anzahl der "Funktionen", für die jede Klasse verantwortlich ist (Zellteilung und ihre Spezialisierung auf den Körper).
Hier möchte ich eine Reihe von Techniken zur Aufrechterhaltung der SRP aufschreiben, aber diese Arbeit ist noch nicht abgeschlossen (ich hoffe, "Hände erreichen"). Aus den offensichtlichen Bereichen, in denen Sie nach diesen Tricks suchen können:
- Entwurfsmuster;
- Verwenden verschiedener spezialisierter Komponentenzweige im Gegensatz zum Erstellen einer Komponente, die alle Anwendungsmethoden erfüllt (Fork on GitHub).
Open-Closed-Prinzip (OCP) Offenes / Geschlossenes Prinzip
Es ist optimal, die Entwicklung des Codes so zu planen, dass der Programmierer zur Implementierung neuer Aufgaben neuen Code hinzufügen muss, während der alte Code keine Änderungen benötigt. Der Code muss offen (offen) zum Hinzufügen und geschlossen (geschlossen) zum Ändern sein.
Der Zweck dieses Prinzips besteht darin, die Arbeitskosten zu minimieren und implizite Fehler zu beseitigen, die aufgrund der folgenden Invarianten in der Entwicklung auftreten:
- [1], [2], [3] zuvor beschrieben,
- Um eine neue Aufgabe zu implementieren, kann der Programmierer neue [Komponenten] hinzufügen oder das Verhalten der alten [Komponenten] ändern.
- Das Hinzufügen von [Komponente] erfordert eine Überprüfung am Ort der neuen Verwendung und kostet den Programmierer Zeit
- Die durch die neue Aufgabe verursachte Änderung des Verhaltens der [Komponente] erfordert eine Überprüfung am Ort der neuen Verwendung und an allen Orten der alten Verwendung, was auch den Zeitaufwand des Programmierers verursacht, und im Fall der veröffentlichten [Komponente] die Arbeit aller Programmierer, die die [Komponente] verwendet haben.
Es ist ratsam, eine Option zum Implementieren einer neuen Aufgabe zu wählen und gleichzeitig den Zeitaufwand des Programmierers zu minimieren.
In der Praxis der Softwareentwicklung sind die Kosten für das Hinzufügen häufiger geringer als die Kosten für Änderungen, was die Verwendung des [Open-Closed] -Prinzips offensichtlich macht. Gleichzeitig gibt es viele Techniken, um die Programmarchitektur in einem Zustand zu halten, in dem bei der Implementierung einer neuen Aufgabe nur [Komponenten] hinzugefügt werden müssen. Diese Arbeit mit Architektur erfordert auch die Zeit eines Programmierers, aber die Praxis in großen Projekten zeigt viel weniger als die Verwendung des Ansatzes, alte Verfahren zu ändern. Und natürlich ist diese Beschreibung der Entwicklung eine Idealisierung. Es gibt fast keine Implementierung der Aufgabe durch einfaches Hinzufügen oder Ändern. Im wirklichen Leben wird eine Mischung dieser Ansätze verwendet, aber OCP betont den Vorteil der Verwendung des Add-Ansatzes.
Und hier möchte ich eine Reihe von Techniken zur Aufrechterhaltung der OCP aufschreiben. Aus den offensichtlichen Bereichen, in denen Sie nach diesen Tricks suchen können:
- Entwurfsmuster;
- DLL-Bibliotheken und Optionen für deren Verteilung, Aktualisierung und Entwicklung von Funktionen;
- Entwicklung von COM-Bibliotheken und Objekten in diesen;
- Entwicklung von Programmiersprachen und Unterstützung für zuvor geschriebenen Code;
- das Gesetzgebungssystem des Staates entwickeln.
Liskov-Substitutionsprinzip (LSP) Barbara-Liskov-Substitutionsprinzip
Dieses Prinzip beschränkt die Verwendung der Erweiterung der Basisschnittstelle [Basis] auf die Implementierung und besagt, dass jede Implementierung der Basisschnittstelle ein Verhalten als Basisschnittstelle aufweisen sollte. Gleichzeitig korrigiert die Basisschnittstelle das erwartete Verhalten an den Verwendungsorten. Und das Vorhandensein eines Unterschieds zum erwarteten Verhalten im Implementierungsverhalten, das durch die Basisschnittstelle behoben wird, führt zur Möglichkeit einer Verletzung der Invariante [2].
Dieses Prinzip basiert und verfeinert die auf Abstraktion basierende Entwurfstechnik. Bei diesem Ansatz wird eine Abstraktion eingeführt - einige grundlegende Eigenschaften und Verhaltensweisen, die für viele Situationen charakteristisch sind, sind festgelegt. Zum Beispiel [Komponentenprozedur] "Zur vorherigen Position bewegen" für Situationen: "Cursor im Text", "Buch in einem Regal", "Element in einem Array", "Füße im Tanz" usw. Und dieser [Komponente] zugewiesen ( oft durch alltägliche Erfahrung und ohne Formalisierung) einige Voraussetzungen und Verhaltensweisen, zum Beispiel: "Das Vorhandensein eines beweglichen Objekts", "Mehrfach wiederholen", "Vorhandensein der Reihenfolge der Elemente", "Vorhandensein fester Positionen von Elementen". LSP erfordert, dass beim Hinzufügen einer neuen Verwendungssituation für [Komponente] alle Voraussetzungen und Einschränkungen der Basis erfüllt sind. Und die Situation „Körner in einer Zuckerdose“ kann durch diese Abstraktion nicht beschrieben werden, obwohl Körner natürlich eine Position haben, es Positionen gibt, in denen die Körner vorher waren, und es möglich ist, sie in ihnen zu bewegen - es gibt nur keine festen Positionen von Elementen.
Der Zweck dieses Prinzips besteht darin, implizite Fehler zu beseitigen, die aufgrund der folgenden Invarianten in der Entwicklung auftreten:
- [1], [2], [3] zuvor beschrieben,
- Die grundlegende [Prozedur] beschreibt ein Verhalten, das in einer Vielzahl von Situationen nützlich ist und die für seine Anwendbarkeit erforderlichen Einschränkungen festlegt.
Das entwickelte [Verfahren] für die Implementierung der Basis muss alle seine Einschränkungen erfüllen, einschließlich der impliziten (informell bereitgestellten).
Sehr oft wird ein Beispiel mit einem Rechteck ([Basis]) und einem Quadrat (Implementierung) gegeben, um dieses Prinzip zu beschreiben. Situationsklasse class CSquare : public CRectangle
. In [base] werden Operationen zum Arbeiten mit Breite und Höhe (Set (Get) Width, Set (Get) Height) eingeführt. Bei der Implementierung von CSquare müssen diese Set-Operationen beide Größen des Objekts ändern. Mir fehlte immer die Erklärung, dass die folgende Einschränkung in [der Basis] „informell“ festgelegt ist: „die Fähigkeit, Breite und Höhe unabhängig voneinander zu verwenden.“ In der CSquare-Implementierung wird verletzt, und an Verwendungsorten wird eine einfache Abfolge von Aktionen verwendet, die auf der Verwendung dieser Unabhängigkeit r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
: r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
r.SetWidth(r.GetWidth()*2); r.SetHeight(r.GetHeight()*2)
- Für die Implementierung erhöht CSquare beide Größen um das Vierfache, anstatt das für CRectangle angenommene Zweifache.
Meiner Meinung nach weist dieses Prinzip auf die Schwierigkeit hin, solche informellen Einschränkungen zu verfolgen, was bei großem Nutzen und hoher Verwendungshäufigkeit des Entwicklungsansatzes "Basisimplementierung" besondere Aufmerksamkeit erfordert.
ISP-Prinzip (Interface Segregation Principle) zur Trennung von Schnittstellen; Prinzip der Abhängigkeitsinversion (DIP) Prinzip der Abhängigkeitsinversion
Diese beiden Prinzipien liegen im Bereich ihrer Anforderungen sehr nahe beieinander. Beide implizieren implizit die Nützlichkeit der Verwendung der kleinstmöglichen Basisschnittstelle als Werkzeug für die Interaktion zweier [Komponenten]: "Client" und "Server" - diese Namen werden lediglich zur Identifizierung ausgewählt. In diesem Fall konzentrieren sich die von den [Komponenten] verwendeten allgemeinen Informationen auf die Basisschnittstelle. Eine [Komponente] ("Server") implementiert die Implementierung der Basisschnittstelle, die andere [Komponente] ("Client") verweist auf diese Implementierung.
Ziel dieser Prinzipien ist es, die Komponentenabhängigkeiten zu minimieren und unabhängige Änderungen an ihrem Code zu ermöglichen, wenn die zugrunde liegende Schnittstelle nicht geändert wird. Die Unabhängigkeit von Komponentenänderungen reduziert die Komplexität und den Arbeitsaufwand, wenn Komponenten die Anforderungen des SRP-Prinzips erfüllen. Ein ähnlicher Ansatz ist möglich, da in der Entwicklung folgende Invarianten existieren:
- [1], [2], [3] zuvor beschrieben,
- Jede [Komponente], die ihrem Verhalten innewohnt, bildet die Grenzen ihrer Verwendung.
- an jedem Verwendungsort der [Komponente] können alle ihre Einschränkungen beteiligt sein,
- Die grundlegende [Komponente] Konsequenz der Definition hat eine geringere Komplexität und Anzahl von Einschränkungen als die [Komponente] Implementierung.
- Jede Änderung an [Komponente] ändert seine Einschränkungen und erfordert die Überprüfung aller Verwendungsorte, was den Zeitaufwand eines Programmierers verursacht
Verwendungsorte der Basis [Komponente] müssen nicht überprüft werden, nachdem Änderungen an der [Komponente] -Implementierung vorgenommen wurden.
Es ist klar, dass es ratsam ist, die "Größe" der Basisschnittstelle zu minimieren, indem nicht verwendete Funktionen und Einschränkungen verworfen werden, wodurch die Implementierung von [Komponenten] durch das Prinzip (LSP) weniger eingeschränkt wird
Das Prinzip des ISP betont die Notwendigkeit der Trennung (Segregation) der Schnittstelle des "Servers", wenn nicht alle veröffentlichten Funktionen von diesem "Client" verwendet werden. In diesem Fall wird nur die vom Kunden benötigte [Basis] zugewiesen und die Minimierung gemeinsam einschränkender Informationen wird sichergestellt.
Und hier möchte ich eine Reihe von Techniken zur Aufrechterhaltung von DIP aufschreiben. Aus den offensichtlichen Bereichen, in denen Sie nach diesen Tricks suchen können:
- Trennung der Klassenbeschreibung in öffentliche und private Teile (und andere Prinzipien der OOP),
- Beschreibung der Interaktion mit einer dynamischen Bibliothek mit einem begrenzten Satz von Funktionen und Objektdeskriptoren;
- Verwenden eines Archivs als Schnittstelle für den Zugriff auf eine Buchbibliothek.
Zurück zur Überschrift werde ich erklären, warum "nicht verstehen" ausgewählt ist. Negation wird hinzugefügt, um durch Fehler die langmütige und meiner Meinung nach sehr nützliche Regel hervorzuheben. Es ist besser, die Technologie nicht zu verstehen und daher nicht zu nutzen, als sie falsch zu verstehen, sie auf Vertrauen zu setzen, Ihre Ressourcen für die Nutzung der Technologie aufzuwenden und infolgedessen keinen nützlichen Auspuff zu bekommen, außer Selbstzufriedenheit und die Möglichkeit, sich mit modischer Technologie zu rühmen.
Vielen Dank für Ihre Aufmerksamkeit.
Referenzen