( Im Sinne dieser Frage meine ichinterface
, wenn ich 'Schnittstelle' sage, das Sprachkonstrukt und nicht eine 'Schnittstelle' im anderen Sinne des Wortes, dh die öffentlichen Methoden, die eine Klasse der Außenwelt bietet, um mit und zu kommunizieren manipuliere es. )
Eine lose Kopplung kann erreicht werden, indem ein Objekt von einer Abstraktion anstelle eines konkreten Typs abhängt.
Dies ermöglicht eine lose Kopplung aus zwei Hauptgründen: 1) Abstraktionen ändern sich weniger wahrscheinlich als konkrete Typen, was bedeutet, dass der abhängige Code weniger wahrscheinlich beschädigt wird. Zur Laufzeit können 2 verschiedene konkrete Typen verwendet werden, da sie alle zur Abstraktion passen. Neue Betontypen können auch später hinzugefügt werden, ohne dass der vorhandene abhängige Code geändert werden muss.
Betrachten Sie beispielsweise eine Klasse Car
und zwei Unterklassen Volvo
und Mazda
.
Wenn Ihr Code von a abhängt Car
, kann er zur Laufzeit entweder a Volvo
oder a verwenden Mazda
. Auch später könnten zusätzliche Unterklassen hinzugefügt werden, ohne dass der abhängige Code geändert werden muss.
Außerdem Car
ist es weniger wahrscheinlich , dass sich - was eine Abstraktion ist - etwas ändert als Volvo
oder Mazda
. Autos sind im Allgemeinen seit geraumer Zeit gleich, aber Volvos und Mazdas ändern sich mit größerer Wahrscheinlichkeit. Dh Abstraktionen sind stabiler als konkrete Typen.
All dies sollte zeigen, dass ich verstehe, was lose Kopplung ist und wie sie erreicht wird, wenn man von Abstraktionen und nicht von Konkretionen abhängt. (Wenn ich etwas falsches geschrieben habe, sagen Sie es bitte).
Was ich nicht verstehe, ist Folgendes:
Abstraktionen können Superklassen oder Interfaces sein.
Wenn ja, warum werden Schnittstellen speziell für ihre Fähigkeit gelobt, lose Kopplungen zuzulassen? Ich verstehe nicht, wie es sich von der Verwendung einer Superklasse unterscheidet.
Die einzigen Unterschiede, die ich sehe, sind: 1- Schnittstellen sind nicht durch einzelne Vererbung beschränkt, aber das hat nicht viel mit dem Thema der losen Kopplung zu tun. 2- Schnittstellen sind abstrakter, da sie überhaupt keine Implementierungslogik haben. Trotzdem verstehe ich nicht, warum das einen so großen Unterschied macht.
Erklären Sie mir bitte, warum Schnittstellen für das Ermöglichen einer losen Kopplung großartig sind, einfache Superklassen dagegen nicht.
quelle
interfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class
(die mich auf den Vergleich mit C ++ leeds, wo Schnittstellen nur Klassen mit rein virtuellen Funktionen sind).Antworten:
Terminologie: Ich beziehe mich auf das Sprachkonstrukt
interface
als Schnittstelle und auf die Schnittstelle eines Typs oder Objekts als Oberfläche (mangels eines besseren Begriffs).Richtig.
Nicht ganz richtig. Gegenwärtige Sprachen erwarten im Allgemeinen nicht, dass sich eine Abstraktion ändert (obwohl es einige Entwurfsmuster gibt, die damit umgehen). Das Trennen von Besonderheiten von allgemeinen Dingen ist Abstraktion. Dies geschieht normalerweise durch eine Abstraktionsebene . Diese Ebene kann in einige andere Details geändert werden, ohne den Code zu beschädigen, der auf dieser Abstraktion aufbaut - es wird eine lose Kopplung erreicht. Non-OOP-Beispiel: Eine
sort
Routine wird möglicherweise von Quicksort in Version 1 in Tim Sort in Version 2 geändert. Code, der nur vom zu sortierenden Ergebnis abhängt (dh auf dersort
Abstraktion aufbaut ), wird daher von der eigentlichen Sortierungsimplementierung abgekoppelt.Was ich oben als Oberfläche bezeichnet habe, ist der allgemeine Teil einer Abstraktion. In OOP kommt es jetzt vor, dass ein Objekt manchmal mehrere Abstraktionen unterstützen muss. Ein nicht ganz optimales Beispiel: Java
java.util.LinkedList
unterstützt sowohl dieList
Schnittstelle, bei der es um die Abstraktion "geordnete, indexierbare Sammlung" geht, als auch dieQueue
Schnittstelle, bei der es sich (grob gesagt) um die Abstraktion "FIFO" handelt.Wie kann ein Objekt mehrere Abstraktionen unterstützen?
C ++ hat keine Schnittstellen, aber mehrere Vererbungen, virtuelle Methoden und abstrakte Klassen. Eine Abstraktion kann dann als eine abstrakte Klasse (dh eine Klasse, die nicht sofort instanziiert werden kann) definiert werden, die virtuelle Methoden deklariert, aber nicht definiert. Klassen, die die Besonderheiten einer Abstraktion implementieren, können dann von dieser abstrakten Klasse erben und die erforderlichen virtuellen Methoden implementieren.
Das Problem hierbei ist, dass Mehrfachvererbung zum Diamantproblem führen kann , wobei die Reihenfolge, in der Klassen nach einer Methodenimplementierung durchsucht werden (MRO: method resolution order), zu „Widersprüchen“ führen kann. Darauf gibt es zwei Antworten:
Definieren Sie eine vernünftige Reihenfolge und lehnen Sie Bestellungen ab, die nicht sinnvoll linearisiert werden können. Der C3 MRO ist ziemlich vernünftig und funktioniert gut. Es wurde 1996 veröffentlicht.
Nehmen Sie den einfachen Weg und lehnen Sie die Mehrfachvererbung ab.
Java entschied sich für die letztere Option und entschied sich für eine einzelne Verhaltensvererbung. Wir benötigen jedoch weiterhin die Fähigkeit eines Objekts, mehrere Abstraktionen zu unterstützen. Daher müssen Schnittstellen verwendet werden, die keine Methodendefinitionen, sondern nur Deklarationen unterstützen.
Das Ergebnis ist, dass die MRO offensichtlich ist (sehen Sie sich jede Superklasse in der richtigen Reihenfolge an) und dass unser Objekt mehrere Oberflächen für eine beliebige Anzahl von Abstraktionen haben kann.
Dies stellt sich als eher unbefriedigend heraus, da ziemlich oft ein bisschen Verhalten Teil der Oberfläche ist. Betrachten Sie eine
Comparable
Schnittstelle:Dies ist sehr benutzerfreundlich (eine nette API mit vielen praktischen Methoden), aber mühsam zu implementieren. Wir möchten, dass die Schnittstelle nur
cmp
die anderen Methoden in Bezug auf diese eine erforderliche Methode einschließt und automatisch implementiert. Mixins , aber vor allem Traits [ 1 ], [ 2 ] lösen dieses Problem, ohne in die Fallen der Mehrfachvererbung zu geraten.Dies erfolgt durch die Definition einer Merkmalszusammensetzung, sodass die Merkmale nicht tatsächlich am MRO teilnehmen, sondern die definierten Methoden in der implementierenden Klasse zusammengefasst werden.
Die
Comparable
Schnittstelle könnte in Scala als ausgedrückt werdenWenn eine Klasse dieses Merkmal verwendet, werden die folgenden Methoden zur Klassendefinition hinzugefügt:
So
Inty(4) cmp Inty(6)
wäre-2
undInty(4) lt Inty(6)
wäretrue
.In vielen Sprachen werden bestimmte Merkmale unterstützt, und in jeder Sprache, die über ein „Metaobject Protocol (MOP)“ verfügt, können Merkmale hinzugefügt werden. Das jüngste Java 8-Update fügte Standardmethoden hinzu, die den Merkmalen ähneln (Methoden in Schnittstellen können Fallback-Implementierungen aufweisen, sodass die Implementierung dieser Methoden durch Klassen optional ist).
Leider handelt es sich bei Merkmalen um eine relativ neue Erfindung (2002), die in den größeren Hauptsprachen daher eher selten vorkommt.
quelle
Erstens sind Subtypisierung und Abstraktion zwei verschiedene Dinge. Subtypisierung bedeutet lediglich, dass ich Werte eines Typs durch Werte eines anderen Typs ersetzen kann - keiner der Typen muss abstrakt sein.
Vor allem haben Unterklassen eine direkte Abhängigkeit von den Implementierungsdetails ihrer Oberklasse. Das ist die stärkste Kopplung, die es gibt. Wenn die Basisklasse nicht unter Berücksichtigung der Vererbung entworfen wurde, können Änderungen an der Basisklasse, die ihr Verhalten nicht ändern, Unterklassen dennoch beschädigen, und es gibt keine Möglichkeit, a priori zu ermitteln, ob eine Beschädigung auftritt. Dies ist als das fragile Basisklassenproblem bekannt .
Das Implementieren einer Schnittstelle koppelt Sie an nichts anderes als an die Schnittstelle selbst, die kein Verhalten enthält.
quelle
you want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named C
, dass Sie annehmen, dass Klassen nicht abstrakt sind. Eine Abstraktion ist alles, was Implementierungsdetails verbirgt. Eine Klasse mit privaten Feldern ist also genauso abstrakt wie eine Schnittstelle mit denselben öffentlichen Methoden.Es gibt eine Kopplung zwischen Eltern- und Kindklassen, da das Kind vom Elternteil abhängt.
Nehmen wir an, wir haben eine Klasse A und Klasse B erbt davon. Wenn wir in die Klasse A gehen und Dinge ändern, wird auch die Klasse B geändert.
Nehmen wir an, wir haben eine Schnittstelle I und Klasse B implementiert sie. Wenn wir die Schnittstelle I ändern, bleibt die Klasse B unverändert, obwohl Klasse B sie möglicherweise nicht mehr implementiert.
quelle