In der Java-Sprache sind abstrakte Klasse und Schnittstelle zwei Mechanismen, die die Definition abstrakter Klassen unterstützen. Gerade aufgrund der Existenz dieser beiden Mechanismen erhält Java leistungsstarke objektorientierte Fähigkeiten. Es gibt große Ähnlichkeiten zwischen abstrakten Klassen und Schnittstellen in Bezug auf die Unterstützung der Definition abstrakter Klassen und sie können sogar untereinander ersetzt werden. Daher scheinen viele Entwickler bei der Definition abstrakter Klassen eher locker mit der Wahl der abstrakten Klasse und Schnittstelle umzugehen. Tatsächlich gibt es immer noch einen großen Unterschied zwischen den beiden. Ihre Wahl spiegelt sogar das Verständnis der Natur des Problembereichs wider und ob das Verständnis der Entwurfsabsicht richtig und vernünftig ist. In diesem Artikel werden die Unterschiede zwischen ihnen analysiert und versucht, Entwicklern eine Grundlage für die Wahl zwischen den beiden zu bieten.
Abstrakte Klassen verstehen
Die abstrakte Klasse und die Schnittstelle werden beide zum Definieren abstrakter Klassen in der Java-Sprache verwendet (die abstrakte Klasse in diesem Artikel wird nicht von der abstrakten Klasse übersetzt, sie stellt einen abstrakten Körper dar und die abstrakte Klasse wird zum Definieren abstrakter Klassen in der Java-Sprache verwendet) A (Methode, bitte achten Sie auf die Unterscheidung). Was ist also eine abstrakte Klasse und welche Vorteile kann uns die Verwendung abstrakter Klassen bringen?
Im objektorientierten Konzept wissen wir, dass alle Objekte durch Klassen dargestellt werden, aber das Gegenteil ist nicht der Fall. Nicht alle Klassen werden zur Beschreibung von Objekten verwendet. Wenn eine Klasse nicht genügend Informationen zur Beschreibung eines bestimmten Objekts enthält, ist eine solche Klasse eine abstrakte Klasse. Abstrakte Klassen werden oft verwendet, um abstrakte Konzepte darzustellen, die wir aus der Analyse und Gestaltung von Problembereichen ableiten. Sie sind Abstraktionen einer Reihe spezifischer Konzepte, die unterschiedlich aussehen, aber im Wesentlichen gleich sind. Wenn wir beispielsweise eine Grafikbearbeitungssoftware entwickeln, werden wir feststellen, dass es im Problembereich einige spezifische Konzepte gibt, die unterschiedlich sind, aber alle zum Konzept der Form gehören Im Problembereich handelt es sich um ein abstraktes Konzept. Gerade weil abstrakte Konzepte keine entsprechenden konkreten Konzepte im Problembereich haben, können die zur Darstellung abstrakter Konzepte verwendeten abstrakten Klassen nicht instanziiert werden.
Im objektorientierten Bereich werden abstrakte Klassen hauptsächlich zum Verbergen von Typen verwendet. Wir können eine abstrakte Beschreibung eines festen Satzes von Verhaltensweisen konstruieren, aber dieser Satz von Verhaltensweisen kann beliebig viele mögliche konkrete Implementierungen haben. Diese abstrakte Beschreibung ist die abstrakte Klasse, und diese Menge aller möglichen konkreten Implementierungen wird durch alle möglichen abgeleiteten Klassen repräsentiert. Module können auf einem abstrakten Körper arbeiten. Da ein Modul auf einer festen Abstraktion basiert, kann es nicht gleichzeitig geändert werden. Das Verhalten dieses Moduls kann auch durch Ableitung von dieser Abstraktion erweitert werden. Leser, die mit OCP vertraut sind, müssen wissen, dass für die Umsetzung von OCP (Open-Closed Principle), einem der Kernprinzipien des objektorientierten Designs, abstrakte Klassen der Schlüssel sind.
Betrachtung abstrakter Klassen und Schnittstellen auf der Ebene der grammatikalischen Definition
Auf grammatikalischer Ebene bietet die Java-Sprache verschiedene Definitionsmethoden für abstrakte Klassen und Schnittstellen. Das Folgende ist ein Beispiel für die Definition einer abstrakten Klasse namens Demo, um diesen Unterschied zu veranschaulichen.
Die Art und Weise, die abstrakte Demo-Klasse mithilfe der abstrakten Klasse zu definieren, ist wie folgt:
abstrakte Klassendemo{
abstrakte void method1();
abstrakte void method2();
…
}
Die Methode zum Definieren der abstrakten Demo-Klasse mithilfe der Schnittstelle ist wie folgt:
Schnittstellen-Demo{
void method1();
void method2();
…
}
In der abstrakten Klassenmethode kann Demo über eigene Datenelemente oder nicht-abstrakte Elementmethoden verfügen. In der Schnittstellenmethode kann Demo nur statische Datenelemente haben, die nicht geändert werden können (dh es müssen statische Datenelemente sein). Mitglieder werden im Allgemeinen nicht in der Schnittstelle definiert) und alle Mitgliedsmethoden sind abstrakt. In gewissem Sinne ist die Schnittstelle eine spezielle Form einer abstrakten Klasse.
Aus Programmiersicht können sowohl abstrakte Klassen als auch Schnittstellen verwendet werden, um die Idee des „Design by Contract“ umzusetzen. Es gibt jedoch immer noch einige Unterschiede in der spezifischen Verwendung.
Erstens stellt eine abstrakte Klasse eine Vererbungsbeziehung in der Java-Sprache dar, und eine Klasse kann die Vererbungsbeziehung nur einmal verwenden (da Java keine Mehrfachvererbung unterstützt – Hinweis zur Übertragung). Eine Klasse kann jedoch mehrere Schnittstellen implementieren. Vielleicht ist dies eine Kompromissüberlegung der Entwickler der Java-Sprache, wenn sie die Unterstützung von Mehrfachvererbung durch Java in Betracht ziehen.
Zweitens können wir bei der Definition einer abstrakten Klasse das Standardverhalten der Methode zuweisen. Bei der Definition der Schnittstelle können Methoden jedoch kein Standardverhalten aufweisen. Um diese Einschränkung zu umgehen, müssen Delegaten verwendet werden. Dies erhöht jedoch die Komplexität und verursacht manchmal große Probleme.
Es besteht ein weiteres ernstes Problem darin, dass das Standardverhalten in einer abstrakten Klasse nicht definiert werden kann, da dies zu Wartungsproblemen führen kann. Denn wenn Sie später die Schnittstelle der Klasse (normalerweise durch eine abstrakte Klasse oder Schnittstelle dargestellt) ändern möchten, um sie an neue Situationen anzupassen (z. B. das Hinzufügen neuer Methoden oder das Hinzufügen neuer Parameter zu bereits verwendeten Methoden), ist dies sehr problematisch kann viel Zeit in Anspruch nehmen (insbesondere wenn viele abgeleitete Klassen vorhanden sind). Wenn die Schnittstelle jedoch über eine abstrakte Klasse implementiert wird, müssen Sie möglicherweise nur das in der abstrakten Klasse definierte Standardverhalten ändern.
Wenn das Standardverhalten in einer abstrakten Klasse nicht definiert werden kann, erscheint in ähnlicher Weise dieselbe Methodenimplementierung in jeder abgeleiteten Klasse der abstrakten Klasse, was gegen das Prinzip „Eine Regel, ein Ort“ verstößt, was zu einer Codeduplizierung führt, was ebenfalls schädlich ist die Zukunft. Seien Sie daher bei der Wahl zwischen abstrakter Klasse und Schnittstelle sehr vorsichtig.
Betrachtung abstrakter Klassen und Schnittstellen auf der Ebene des Designkonzepts
Oben wird hauptsächlich der Unterschied zwischen abstrakter Klasse und Schnittstelle aus der Perspektive der grammatikalischen Definition und Programmierung erörtert. Die Unterschiede auf diesen Ebenen sind relativ gering und nicht wesentlich. In diesem Abschnitt wird der Unterschied zwischen abstrakter Klasse und Schnittstelle auf einer anderen Ebene analysiert: die Designkonzepte, die sich in den beiden widerspiegeln. Der Autor glaubt, dass wir nur durch eine Analyse auf dieser Ebene das Wesen der beiden Konzepte verstehen können.
Wie bereits erwähnt, verkörpert die abstrakte Klasse eine Vererbungsbeziehung in der Java-Sprache. Um die Vererbungsbeziehung sinnvoll zu gestalten, muss zwischen der übergeordneten Klasse und der abgeleiteten Klasse eine „Ist-ein“-Beziehung bestehen Die abgeleiteten Klassen haben das gleiche Konzept. Im Wesentlichen sollte es dasselbe sein. Dies ist bei Schnittstellen nicht der Fall. Der Implementierer der Schnittstelle und die Schnittstellendefinition müssen nicht konzeptionell konsistent sein, sondern implementieren nur den durch die Schnittstelle definierten Vertrag. Um die Diskussion verständlicher zu machen, wird im Folgenden ein einfaches Beispiel illustriert.
Betrachten Sie ein solches Beispiel. Angenommen, es gibt ein abstraktes Konzept für die Tür in unserem Problembereich. Zu diesem Zeitpunkt können wir einen Typ definieren, der das abstrakte Konzept durch eine abstrakte Klasse oder Schnittstelle darstellt Methoden sind wie folgt:
Verwenden Sie eine abstrakte Klasse, um die Tür zu definieren:
abstrakte Klasse Door{
abstrakte Leere open();
abstrakte Leere close();
}
Definieren Sie die Tür mithilfe der Schnittstellenmethode:
Schnittstelle Tür{
void open();
void close();
}
Andere spezifische Türtypen können die mit der abstrakten Klassenmethode definierte Tür erweitern oder die mit der Schnittstellenmethode definierte Tür implementieren. Es scheint, dass es keinen großen Unterschied zwischen der Verwendung einer abstrakten Klasse und einer Schnittstelle gibt.
Die Tür muss nun über eine Alarmfunktion verfügen. Wie sollen wir die Klassenstruktur für dieses Beispiel entwerfen (in diesem Beispiel dient sie hauptsächlich dazu, den Unterschied im Designkonzept zwischen abstrakter Klasse und Schnittstelle zu zeigen, und andere irrelevante Probleme wurden vereinfacht oder ignoriert)? Mögliche Lösungen sind unten aufgeführt und diese verschiedenen Optionen werden auf der Ebene des Designkonzepts analysiert.
Lösung eins:
Fügen Sie einfach wie folgt eine Alarmmethode zur Definition von Tür hinzu:
abstrakte Klasse Door{
abstrakte Leere open();
abstrakte Leere close();
abstrakter Leerealarm();
}
oder
Schnittstelle Tür{
void open();
void close();
Void-Alarm();
}
Dann ist die AlarmTür mit Alarmfunktion wie folgt definiert:
Klasse AlarmDoor erweitert Door{
void open(){…}
void close(){…}
void alarm(){…}
}
oder
Klasse AlarmDoor implementiert Door{
void open(){…}
void close(){…}
void alarm(){…}
}
Diese Methode verstößt gegen das ISP (Interface Segregation Principle), ein Kernprinzip des objektorientierten Designs. In der Definition von Door wird die inhärente Verhaltensmethode des Door-Konzepts selbst mit der Verhaltensmethode eines anderen Konzepts „Alarm“ vermischt. Ein dadurch verursachtes Problem besteht darin, dass sich Module, die nur auf dem Konzept „Tür“ basieren, aufgrund von Änderungen im Konzept „Alarm“ ändern (z. B. durch Ändern der Parameter der Alarmmethode) und umgekehrt.
Lösung zwei:
Da Öffnen, Schließen und Alarm zu zwei unterschiedlichen Konzepten gehören, sollten sie gemäß dem ISP-Prinzip in abstrakten Klassen definiert werden, die diese beiden Konzepte darstellen. Die Definitionsmethoden sind: Beide Konzepte werden mit der abstrakten Klassenmethode definiert; ein Konzept wird mit der abstrakten Klassenmethode definiert und das andere Konzept wird mit der Schnittstellenmethode definiert.
Da die Java-Sprache keine Mehrfachvererbung unterstützt, ist es offensichtlich nicht möglich, beide Konzepte mithilfe einer abstrakten Klasse zu definieren. Die beiden letztgenannten Methoden sind beide machbar, aber ihre Wahl spiegelt das Verständnis des Wesens des Konzepts im Problembereich wider und ob die Widerspiegelung der Entwurfsabsicht richtig und sinnvoll ist. Lassen Sie uns einzeln analysieren und erklären.
Wenn beide Konzepte mithilfe der Schnittstellenmethode definiert werden, spiegelt dies zwei Probleme wider: 1. Wir verstehen den Problembereich möglicherweise nicht klar. Handelt es sich bei AlarmDoor im Wesentlichen um eine Tür oder einen Alarm? 2. Wenn es kein Problem mit unserem Verständnis der Problemdomäne gibt, zum Beispiel: Durch die Analyse der Problemdomäne stellen wir fest, dass AlarmDoor konzeptionell mit Door übereinstimmt, dann können wir unsere Designabsicht bei der Umsetzung nicht korrekt offenlegen , weil Die Definitionen dieser beiden Konzepte (beide mithilfe der Schnittstellenmethode definiert) spiegeln nicht die obige Bedeutung wider.
Wenn wir die Problemdomäne wie folgt verstehen: AlarmDoor ist konzeptionell im Wesentlichen eine Tür und hat auch die Funktion, einen Alarm auszulösen. Wie sollten wir es gestalten und umsetzen, um unsere Bedeutung klar widerzuspiegeln? Wie bereits erwähnt, stellt eine abstrakte Klasse eine Vererbungsbeziehung in der Java-Sprache dar, und die Vererbungsbeziehung ist im Wesentlichen eine „Ist-ein“-Beziehung. Für das Konzept der Tür sollten wir also die abstrakte Klassenmethode verwenden, um es zu definieren. Darüber hinaus verfügt AlarmDoor über eine Alarmfunktion, was bedeutet, dass es das im Alarmkonzept definierte Verhalten vervollständigen kann, sodass das Alarmkonzept über die Schnittstelle definiert werden kann. Wie unten gezeigt:
abstrakte Klasse Door{
abstrakte Leere open();
abstrakte Leere close();
}
interfaceAlarm{
Void-Alarm();
}
Klasse Alarm Tür erweitert Tür implementiert Alarm{
void open(){…}
void close(){…}
void alarm(){…}
}
Diese Implementierungsmethode kann grundsätzlich unser Verständnis der Problemdomäne klar widerspiegeln und unsere Entwurfsabsichten korrekt offenbaren. Tatsächlich stellt die abstrakte Klasse die „Ist-a“-Beziehung und die Schnittstelle die „Like-a“-Beziehung dar. Dies basiert natürlich auf dem Verständnis der Problemdomäne Beispiel: Wenn AlarmDoor vom Konzept her im Wesentlichen ein Alarm ist und die Funktion einer Tür hat, muss die obige Definition umgekehrt werden.
Zusammenfassung
1. Die abstrakte Klasse stellt eine Vererbungsbeziehung in der Java-Sprache dar, und eine Klasse kann die Vererbungsbeziehung nur einmal verwenden. Eine Klasse kann jedoch mehrere Schnittstellen implementieren.
2. In der abstrakten Klasse können Sie Ihre eigenen Datenelemente haben, und Sie können auch nicht abstrakte Elementmethoden haben, aber in der Schnittstelle können Sie nur statische Datenelemente haben, die nicht geändert werden können (das heißt, sie müssen statisch endgültig sein). aber in Schnittstelle Datenelemente sind im Allgemeinen nicht definiert) und alle Mitgliedsmethoden sind abstrakt.
3.Abstrakte Klasse und Schnittstelle spiegeln unterschiedliche Designkonzepte wider. Tatsächlich repräsentiert die abstrakte Klasse die „ist-ein“-Beziehung und die Schnittstelle repräsentiert die „wie-ein“-Beziehung.
4. Klassen, die abstrakte Klassen und Schnittstellen implementieren, müssen alle darin enthaltenen Methoden implementieren. Abstrakte Klassen können nicht abstrakte Methoden haben. In der Schnittstelle dürfen keine Implementierungsmethoden vorhanden sein.
5. Die in der Schnittstelle definierten Variablen sind standardmäßig öffentliche statische Endvariablen und ihr Anfangswert muss angegeben werden, sodass sie in der Implementierungsklasse weder neu definiert noch ihre Werte geändert werden können.
6. Variablen in abstrakten Klassen sind standardmäßig benutzerfreundlich und ihre Werte können in Unterklassen neu definiert oder neu zugewiesen werden.
7. Die Methoden in der Schnittstelle sind standardmäßig vom öffentlichen und abstrakten Typ.
abschließend
Abstrakte Klasse und Schnittstelle sind zwei Möglichkeiten, abstrakte Klassen in der Java-Sprache zu definieren, und sie sind sehr ähnlich. Ihre Auswahl spiegelt jedoch häufig das Verständnis des Wesens des Konzepts im Problembereich und die Frage wider, ob die Reflexion der Entwurfsabsicht richtig und angemessen ist, da sie unterschiedliche Beziehungen zwischen Konzepten zum Ausdruck bringen (obwohl sie alle die erforderlichen Funktionen erreichen können). Dies ist eigentlich eine Art idiomatischer Sprachgebrauch. Ich hoffe, dass die Leser ihn sorgfältig verstehen können.