Heim >Java >javaLernprogramm >Sieben Prinzipien, denen Java-Entwurfsmuster folgen
In den letzten Jahren haben sich Menschen aktiv für Designmuster eingesetzt und diese verwendet. Der Hauptgrund besteht darin, die Wiederverwendbarkeit des Codes zu erreichen und die Wartbarkeit des Codes zu verbessern. Die Implementierung von Designmustern folgt einigen Prinzipien, um den Zweck der Wiederverwendbarkeit von Code zu erreichen und die Wartbarkeit zu erhöhen. Designmuster sind sehr inspirierend für das Verständnis der drei Hauptmerkmale der Objektorientierung. Ohne einen Blick auf Designmuster ist es schwierig, sie im Detail zu verstehen. zu den Vorteilen der objektorientierten Entwicklung. Zu Beginn des Lernens ist es schwierig, diese Modi zu integrieren. Daher müssen wir vor dem Codieren mehr nachdenken und dann mit dem Üben des Codierens beginnen, wenn wir genug nachgedacht haben. Im Folgenden sind sieben Prinzipien aufgeführt, denen Entwurfsmuster folgen sollten:
1 Schließung.
Das Open-Closed-Prinzip bedeutet, dass Sie beim Entwerfen immer berücksichtigen und versuchen sollten, diese Klasse gut genug zu machen. Wenn neue Anforderungen eintreten, werden wir sie hinzufügen . Das ist es, der ursprüngliche Code wird sich nicht ändern, wenn er kann. Dieses Prinzip weist zwei Merkmale auf: das eine ist „offen für Erweiterungen“ und das andere ist „geschlossen für Veränderungen“. Bei Bedarf werden Änderungen am Programm durch Hinzufügen von neuem Code vorgenommen, anstatt vorhandenen Code zu ändern. Dies ist der Geist des „Offen-Geschlossen-Prinzips“
Am Anfang bestand die Anforderung beispielsweise nur darin, ein Additionsprogramm zu schreiben. Kurz nachdem es in der Client-Klasse abgeschlossen war, trat die Änderung nicht auf Zu diesem Zeitpunkt bestand die Anforderung darin, eine Subtraktionsfunktion hinzuzufügen. Zu diesem Zeitpunkt werden Sie feststellen, dass das Hinzufügen von Funktionen eine Änderung der ursprünglichen Klasse erfordert, was gegen das Offen-Geschlossen-Prinzip verstößt. Daher sollten Sie eine Rekonstruktion des Programms durch Hinzufügen einer abstrakten Berechnung in Betracht ziehen Klasse und unter Verwendung einiger objektorientierter Mittel wie Vererbung, Dynamik usw. Durch die Isolierung spezifischer Additionen und Subtraktionen von der Clientkopplung können Anforderungen weiterhin erfüllt und Änderungen gehandhabt werden. Wenn Sie zu diesem Zeitpunkt Multiplikations- und Divisionsfunktionen hinzufügen müssen, müssen Sie die Client- und Additions- und Subtraktionsklassen nicht ändern. Fügen Sie stattdessen einfach Multiplikations- und Divisionsunterklassen hinzu.
Es ist unmöglich, Änderungen vollständig abzuschließen, egal wie „geschlossen“ das Modul ist. Da es unmöglich ist, vollständig geschlossen zu werden, muss der Designer entscheiden, welche Änderungen das von ihm entworfene Modul vornehmen soll geschlossen sein. Er muss zunächst die Arten von Änderungen erraten, die am wahrscheinlichsten auftreten, und dann Abstraktionen konstruieren, um diese Änderungen zu isolieren. Wenn wir zum ersten Mal Code schreiben, gehen wir davon aus, dass keine Änderungen vorgenommen werden. Wenn dies der Fall ist, erstellen wir Abstraktionen, um zu verhindern, dass dieselben Änderungen in der Zukunft auftreten.
Wir möchten kurz nach Beginn der Entwicklung über mögliche Änderungen Bescheid wissen. Je länger Sie warten, um herauszufinden, welche Änderungen aufgetreten sind, desto schwieriger ist es, die richtige Abstraktion zu erstellen. Das Offen-Geschlossen-Prinzip ist der Kern des objektorientierten Designs und kann die enormen Vorteile der objektorientierten Technologie mit sich bringen, nämlich Wartbarkeit, Skalierbarkeit, Wiederverwendbarkeit und Flexibilität. Entwickler sollten nur die Teile des Programms abstrahieren, die häufigen Änderungen unterliegen. Es ist jedoch auch keine gute Idee, jeden Teil der Anwendung absichtlich abzustrahieren. Die Ablehnung unausgereifter Abstraktionen ist ebenso wichtig wie die Abstraktion selbst. Das Open-Closed-Prinzip kann die Korrektheit des vorherigen Codes sicherstellen. Da der vorherige Code nicht geändert wurde, kann sichergestellt werden, dass sich Entwickler auf die Platzierung von Designs auf dem neu erweiterten Code konzentrieren.
Um es einfach auszudrücken: Die Vergangenheit ist Geschichte geworden und kann nicht verändert werden, denn die Zeit lässt sich nicht zurückdrehen, aber was Sie jetzt oder morgen vorhaben, können Sie selbst entscheiden (also erweitern).
2. Liskov-Substitutionsprinzip
Definition 1: Wenn für jedes Objekt o1 vom Typ T1 ein Objekt o2 vom Typ T2 vorhanden ist, gilt für T1 das Verhalten aller definierten Programme P nicht Wenn sich alle Objekte o1 durch o2 ersetzen, ist Typ T2 ein Untertyp von Typ T1.
Definition 2: Untertypen müssen in der Lage sein, ihre übergeordneten Typen zu ersetzen.
Beschreibung: Wenn eine Software-Entität eine übergeordnete Klasse verwendet, muss diese auf ihre Unterklassen anwendbar sein und kann den Unterschied zwischen übergeordneten Klassenobjekten und untergeordneten Klassenobjekten nicht erkennen. Mit anderen Worten, in der Software werden die übergeordneten Klassen ersetzt mit seinen Unterklassen, und das Verhalten des Programms ändert sich nicht Beispiel: In der biologischen Klassifizierung ist ein Pinguin eine Art Vogel, aber in der Programmierwelt kann ein Pinguin nicht von einem Vogel erben. Im objektorientierten Design verfügt die Unterklasse über alle nichtprivaten Verhaltensweisen und Attribute der übergeordneten Klasse. Vögel können fliegen, Pinguine können jedoch nicht fliegen, sodass Pinguine keine Vögel erben können.
Nur wenn die Unterklasse die übergeordnete Klasse ersetzen kann und die Funktionen der Softwareeinheit nicht beeinträchtigt werden, kann die übergeordnete Klasse wirklich wiederverwendet werden und die Unterklasse kann auch neue Verhaltensweisen auf der Grundlage der übergeordneten Klasse hinzufügen Das Prinzip der Substitution ermöglicht Vererbung und Wiederverwendung. Gerade wegen der Ersetzbarkeit von Untertypen können Module, die den übergeordneten Klassentyp verwenden, ohne Änderung erweitert werden. Wie können wir sonst über das Öffnen zur Erweiterung und das Schließen zur Änderung sprechen?
Das Liskov-Ersetzungsprinzip ist beliebt. Vereinfacht ausgedrückt: Unterklassen können die Funktionen der übergeordneten Klasse erweitern, aber die ursprünglichen Funktionen der übergeordneten Klasse nicht ändern. Es enthält die folgenden 4 Bedeutungsebenen:
1. Unterklassen können abstrakte Methoden der übergeordneten Klasse implementieren, jedoch keine nicht-abstrakten Methoden der übergeordneten Klasse überschreiben.
2. Unterklassen können ihre eigenen einzigartigen Methoden hinzufügen.
3. Wenn eine Methode einer Unterklasse eine Methode einer übergeordneten Klasse überschreibt, sind die Voraussetzungen der Methode (d. h. die formalen Parameter der Methode) lockerer als die Eingabeparameter der Methode der übergeordneten Klasse.
4. Wenn eine Methode einer Unterklasse eine abstrakte Methode einer übergeordneten Klasse implementiert, sind die Nachbedingungen der Methode (d. h. der Rückgabewert der Methode) strenger als die der übergeordneten Klasse.
Es scheint unglaublich, denn wir werden feststellen, dass wir in unserer eigenen Programmierung oft gegen das Liskov-Substitutionsprinzip verstoßen, das Programm aber trotzdem gut läuft. Jeder wird sich also fragen: Welche Konsequenzen hat es, wenn ich darauf bestehe, das Liskov-Substitutionsprinzip nicht zu befolgen?
Die Konsequenz ist: Die Wahrscheinlichkeit von Problemen mit dem von Ihnen geschriebenen Code wird erheblich erhöht.
3. Prinzip der Abhängigkeitsinversion
Definition: High-Level-Module sollten nicht von ihren Abstraktionen abhängen; Abstraktionen sollten nicht von Details abhängen Abstraktionen. Das heißt, Programm für die Schnittstelle, nicht für die Implementierung
Abhängigkeitsumkehr bedeutet eigentlich, dass sich niemand auf jemand anderen verlassen sollte, außer auf die vereinbarte Schnittstelle, jeder kann flexibel und frei sein. Man kann sagen, dass die Abhängigkeitsumkehr ein Zeichen für objektorientiertes Design ist. Es spielt keine Rolle, welche Sprache zum Schreiben des Programms verwendet wird. Wenn Sie beim Schreiben darüber nachdenken, wie Sie die Abstraktion und nicht die Details, also alle Abhängigkeiten, programmieren Im Programm endet die Klasse oder Schnittstelle als objektorientiertes Design, andernfalls handelt es sich um prozedurales Design. Wenn die verschiedenen Komponenten oder Klassen des Designs voneinander abhängen, ist der Kopplungsgrad hoch und die Wartung und Erweiterung wird schwierig. Dies spiegelt nicht die Vorteile der Objektorientierung wider.
Das Abhängigkeitsinversionsprinzip ist wie ein Team. Es gibt eine Nachfragegruppe, eine Entwicklungsgruppe und eine Testgruppe. Die Entwicklungsgruppe und die Testgruppe sind alle mit den gleichen Anforderungen konfrontiert und erledigen ihre eigene entsprechende Arbeit Testgruppe. Erstellen Sie Testfälle entsprechend den vom Entwicklungsteam verstandenen Anforderungen. Das Ziel ist für alle gleich, sicherzustellen, dass das Produkt pünktlich online geht. Die Nachfrage hängt nicht von Entwicklung und Tests ab.
Das Prinzip der Abhängigkeitsinversion basiert auf der Tatsache, dass abstrakte Dinge viel stabiler sind als die Variabilität von Details. Eine auf Abstraktion basierende Architektur ist viel stabiler als eine auf Details basierende Architektur. In Java bezieht sich Abstraktion auf Schnittstellen oder abstrakte Klassen, und Details sind spezifische Implementierungsklassen. Der Zweck der Verwendung von Schnittstellen oder abstrakten Klassen besteht darin, Spezifikationen und Verträge zu formulieren, ohne dass bestimmte Operationen erforderlich sind, und überlässt die Aufgabe, die Details ihrer Implementierungsklasse anzuzeigen vollständig.
Die zentrale Idee des Abhängigkeitsinversionsprinzips ist die schnittstellenorientierte Programmierung. Es gibt drei Möglichkeiten, Abhängigkeiten zu übertragen. Es gibt auch zwei Übertragungsmethoden: Konstruktormethodenübertragung und Settermethodenübertragung Ich glaube, dass jeder, der das Spring-Framework verwendet hat, mit der Abhängigkeitsbereitstellungsmethode vertraut ist.
Bei der eigentlichen Programmierung müssen wir im Allgemeinen die folgenden drei Punkte tun:
Low-Level-Module sollten abstrakte Klassen oder Schnittstellen oder beides haben.
Der deklarierte Typ der Variablen sollte möglichst eine abstrakte Klasse oder Schnittstelle sein.
Befolgen Sie bei der Verwendung der Vererbung das Liskov-Substitutionsprinzip.
Kurz gesagt, das Prinzip der Abhängigkeitsinversion erfordert, dass wir für Schnittstellen programmieren. Wenn wir schnittstellenorientierte Programmierung verstehen, werden wir auch Abhängigkeitsinversion verstehen.
4. Prinzip der Schnittstellentrennung
Die Bedeutung des Prinzips der Schnittstellentrennung ist: Erstellen Sie eine einzige Schnittstelle, bauen Sie keine riesige und aufgeblähte Schnittstelle auf, versuchen Sie, die Schnittstelle so weit wie möglich zu verfeinern. und versuchen Sie, so viele Methoden wie möglich in der Schnittstelle zu verwenden. Mit anderen Worten: Wir müssen für jede Klasse dedizierte Schnittstellen einrichten, anstatt zu versuchen, eine riesige Schnittstelle für alle Klassen zu erstellen, deren Aufruf darauf angewiesen ist. Bei der Programmierung ist es flexibler, auf mehrere spezialisierte Schnittstellen zu setzen, als auf eine umfassende Schnittstelle. Schnittstellen sind „Verträge“, die während des Entwurfs extern festgelegt werden. Durch die dezentralisierte Definition mehrerer Schnittstellen können wir die Verbreitung externer Änderungen verhindern und die Flexibilität und Wartbarkeit des Systems verbessern.
Allerdings werden viele Leute denken, dass das Prinzip der Schnittstellenisolation dem Prinzip der Einzelverantwortung sehr ähnlich ist, aber das ist nicht der Fall. Erstens konzentrierte sich das Prinzip der Einzelverantwortung ursprünglich auf Verantwortlichkeiten, während sich das Prinzip der Schnittstellenisolation auf die Isolierung von Schnittstellenabhängigkeiten konzentrierte. Zweitens schränkt das Prinzip der Einzelverantwortung hauptsächlich Klassen ein, gefolgt von Schnittstellen und Methoden, und es zielt auf die Implementierung und Details des Programms ab, während das Prinzip der Schnittstellenisolation hauptsächlich Schnittstellen einschränkt, hauptsächlich zur Abstraktion und für den Aufbau des Gesamtrahmens des Programms Programm.
Wenn Sie das Schnittstellenisolationsprinzip verwenden, um Schnittstellen einzuschränken, achten Sie auf die folgenden Punkte:
1. Halten Sie die Schnittstelle so klein wie möglich, aber innerhalb der Grenzen. Es ist eine Tatsache, dass eine Verfeinerung der Schnittstelle die Programmierflexibilität verbessern kann. Wenn sie jedoch zu klein ist, führt dies zu zu vielen Schnittstellen und verkompliziert das Design. Es muss also in Maßen geschehen.
2. Passen Sie Dienste für Klassen an, die auf Schnittstellen basieren, indem Sie der aufrufenden Klasse nur die Methoden zur Verfügung stellen, die sie benötigen, und die Methoden ausblenden, die sie nicht benötigt. Nur durch die Konzentration auf die Bereitstellung maßgeschneiderter Dienste für ein Modul können minimale Abhängigkeiten hergestellt werden.
3. Den Zusammenhalt verbessern und externe Interaktion reduzieren. Stellen Sie sicher, dass die Schnittstelle die wenigsten Methoden verwendet, um die meisten Aufgaben zu erledigen.
Bei der Anwendung des Schnittstellenisolationsprinzips muss es moderat sein. Es ist nicht gut, die Schnittstelle zu groß oder zu klein zu gestalten. Beim Entwerfen von Schnittstellen können Sie dieses Prinzip nur dann genau umsetzen, wenn Sie mehr Zeit zum Nachdenken und Planen aufwenden.
4. Das Prinzip der Kombinations-/Aggregationswiederverwendung
bedeutet, dass Zusammensetzung und Aggregation so weit wie möglich anstelle von Vererbungsbeziehungen verwendet werden sollten, um den Zweck der Wiederverwendung zu erreichen.
Dieses Prinzip soll Erstellen Sie ein neues Objekt. Einige vorhandene Objekte werden im Inneren verwendet, um sie zu einem Teil des neuen Objekts zu machen: Das neue Objekt erreicht den Zweck, vorhandene Funktionen wiederzuverwenden, indem es an diese Objekte delegiert.
Tatsächlich besteht der letzte Punkt hier darin, den Unterschied zwischen „has-a“ und „is-a“ zu unterscheiden. Im Vergleich zu Komposition und Aggregation besteht der Nachteil der
-Vererbung darin, dass alle Methoden der übergeordneten Klasse der untergeordneten Klasse ausgesetzt sind. Wenn sich die übergeordnete Klasse ändert, muss sich auch die Unterklasse ändern. Bei der Wiederverwendung von Aggregaten besteht eine geringere Abhängigkeit von anderen Klassen. .
Synthese-/Aggregationswiederverwendung
① Vorteile:
Die einzige Möglichkeit für ein neues Objekt, auf Komponentenobjekte zuzugreifen, ist über die Schnittstelle des Komponentenobjekts.
Diese Art der Wiederverwendung ist eine Black-Box-Wiederverwendung, da die Interna der Komponentenobjektdetails sind für neue Objekte unsichtbar.
Diese Art der Wiederverwendung erfordert weniger Abhängigkeiten.
Diese Wiederverwendung kann dynamisch zur Laufzeit erfolgen, und neue Objekte können Kompositions-/Aggregationsbeziehungen verwenden, um neue Verantwortlichkeiten an die entsprechenden Objekte zu delegieren.
② Nachteile:
Ein System, das auf diese Weise durch Wiederverwendung erstellt wurde, muss mehr Objekte verwalten.
Wiederverwendung der Vererbung
Die neue Implementierung ist einfacher, da die meisten Funktionen der Basisklasse automatisch über die Vererbungsbeziehung in die abgeleitete Klasse eintreten können.
Die geerbte Klasse ändern oder erweitern einfacher umzusetzen.
② Nachteile:
Durch die Wiederverwendung der Vererbung wird die Verpackung zerstört, da die Vererbung die Implementierungsdetails der Basisklasse für die abgeleitete Klasse offenlegt.
Wenn die Implementierung der Basisklasse erfolgt Wenn sich die Klasse ändert, muss sich auch die Implementierung der abgeleiteten Klasse ändern.
Die von der Basisklasse geerbte Implementierung ist statisch, kann zur Laufzeit nicht geändert werden und ist nicht flexibel genug.
6. Demeter-Gesetz
Die Grundidee des Demeter-Gesetzes besteht darin, die lose Kopplung zwischen Klassen zu betonen, desto förderlicher ist sie für die Komplexität Wenn eine schwach gekoppelte Klasse geändert wird, hat dies keine Auswirkungen auf verwandte Klassen. Mit anderen Worten: Das Ausblenden von Informationen fördert die Wiederverwendung von Software.
Seit wir mit der Programmierung in Berührung kamen, kennen wir die allgemeinen Prinzipien der Softwareprogrammierung: geringe Kopplung und hohe Kohäsion. Unabhängig davon, ob es sich um prozessorientierte Programmierung oder objektorientierte Programmierung handelt, kann die Code-Wiederverwendungsrate nur verbessert werden, wenn die Kopplung zwischen Modulen so gering wie möglich gehalten wird. Die Vorteile einer geringen Kopplung liegen auf der Hand, aber wie können wir durch Programmierung eine geringe Kopplung erreichen? Genau das soll mit dem Demeterschen Gesetz erreicht werden.
Das Demeter-Gesetz wird auch als das am wenigsten bekannte Prinzip bezeichnet. Es wurde erstmals 1987 von Ian Holland von der Northeastern University in den Vereinigten Staaten vorgeschlagen. Laienhaft ausgedrückt: Je weniger eine Klasse über die Klassen weiß, von denen sie abhängt, desto besser. Mit anderen Worten, für die abhängige Klasse sollte die Logik, egal wie komplex die Logik ist, so weit wie möglich in der Klasse gekapselt werden, und außer den bereitgestellten öffentlichen Methoden werden keine Informationen nach außen gelangen. Demeters Gesetz hat eine einfachere Definition: Kommunizieren Sie nur mit direkten Freunden. Lassen Sie uns zunächst erklären, was ein direkter Freund ist: Jedes Objekt hat eine Kopplungsbeziehung mit anderen Objekten. Solange zwischen zwei Objekten eine Kopplungsbeziehung besteht, sagen wir, dass die beiden Objekte Freunde sind. Es gibt viele Arten der Kopplung, z. B. Abhängigkeit, Assoziation, Kombination, Aggregation usw. Unter diesen nennen wir Klassen, die in Mitgliedsvariablen, Methodenparametern und Methodenrückgabewerten erscheinen, direkte Freunde, während Klassen, die in lokalen Variablen erscheinen, keine direkten Freunde sind. Mit anderen Worten: Es ist am besten, wenn unbekannte Klassen nicht als lokale Variablen innerhalb der Klasse erscheinen.
Eine Zusammenfassung in einem Satz lautet: Ein Objekt sollte das geringste Wissen über andere Objekte behalten.
7. Prinzip der Einzelverantwortung
Definition: Es darf nicht mehr als ein Grund für einen Klassenwechsel vorliegen. Laienhaft ausgedrückt bedeutet das, dass eine Klasse nur für eine Verantwortung verantwortlich ist und es nur einen Grund für ihre Änderung geben sollte
Wenn es um das Prinzip der Einzelverantwortung geht, werden viele Leute es ablehnen. Weil es zu einfach ist. Selbst wenn ein etwas erfahrener Programmierer noch nie Entwurfsmuster gelesen oder vom Prinzip der Einzelverantwortung gehört hat, wird er sich beim Entwerfen von Software bewusst an dieses wichtige Prinzip halten, da es sich um gesunden Menschenverstand handelt. Bei der Softwareprogrammierung möchte niemand durch die Änderung einer Funktion Fehlfunktionen bei anderen Funktionen verursachen. Die Möglichkeit, dieses Problem zu vermeiden, besteht darin, dem Prinzip der Einzelverantwortung zu folgen. Obwohl das Single-Responsibility-Prinzip so einfach ist und als gesunder Menschenverstand gilt, enthalten selbst Programme, die von erfahrenen Programmierern geschrieben wurden, Code, der gegen dieses Prinzip verstößt. Warum passiert das? Weil es eine Vielzahl von Verantwortlichkeiten gibt. Die sogenannte Verantwortungsdiffusion bedeutet, dass die Verantwortung P aus irgendeinem Grund in feinkörnigere Verantwortlichkeiten P1 und P2 aufgeteilt wird.
Die Vorteile des Single-Responsibility-Prinzips sind:
1. Es kann die Komplexität einer Klasse reduzieren, und ihre Logik ist definitiv viel einfacher verantwortlich für mehrere Verantwortlichkeiten;
Verbesserung der Lesbarkeit von Klassen und der Wartbarkeit des Systems; Reduzieren Sie das Risiko, das durch Änderungen verursacht wird Wenn Sie eine Funktion ändern, können die Auswirkungen auf andere Funktionen erheblich reduziert werden.
Eine Sache, die erklärt werden muss, ist, dass das Prinzip der Einzelverantwortung nicht nur für die objektorientierte Programmierung gilt. Solange es sich um modulare Programmierung handelt, muss dieses wichtige Prinzip befolgt werden.