Wann sollte eine Schnittstelle anstelle einer abstrakten Klasse verwendet werden und umgekehrt?

426

Dies kann eine allgemeine OOP-Frage sein. Ich wollte einen generischen Vergleich zwischen einer Schnittstelle und einer abstrakten Klasse auf der Grundlage ihrer Verwendung durchführen.

Wann möchte man eine Schnittstelle verwenden und wann möchte man eine abstrakte Klasse verwenden ?

Chirantan
quelle
1
Zusätzlich zu den Antworten unten finden Sie eine schöne kurze Liste, wo Sie Schnittstellen bevorzugen möchten und wo nicht: Wann Sie Schnittstellen verwenden sollten: msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80) .aspx
Anthony
Verwenden Sie abstrakt, wenn Sie nicht sicher sind, was die Klasse tun wird. Verwenden Sie die Schnittstelle, wenn Sie sind.
Uğur Gümüşhan
Ich bin gespannt, wie viele Entwickler, die nicht bei Microsoft arbeiten, Schnittstellen in ihrer täglichen Entwicklung definieren und verwenden.
user1451111

Antworten:

429

Ich habe einen Artikel darüber geschrieben:

Abstrakte Klassen und Schnittstellen

Zusammenfassend:

Wenn wir über abstrakte Klassen sprechen, definieren wir Merkmale eines Objekttyps. Angeben, was ein Objekt ist .

Wenn wir über eine Schnittstelle sprechen und Funktionen definieren, die wir bereitstellen möchten, sprechen wir über den Abschluss eines Vertrags darüber, was das Objekt tun kann.

Jorge Córdoba
quelle
114
Das war sehr hilfreich : Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk". Vielen Dank
Aexl
2
Ihr Link scheint tot zu sein.
Kim Ahlstrøm Meyn Mathiassen
Die folgende Erklärung von Alex zu: Der Unterschied zwischen der Beschreibung nur implementierter Funktionen und der Beschreibung des gespeicherten Zustands scheint eine bessere Antwort auf diese Frage zu sein, da die Unterschiede nicht nur philosophisch sind.
Duncan Malashock
1
Duncan Malashock, nicht wirklich. Jorges Antwort ist die bessere. Alex 'Antwort konzentriert sich auf die Mechanik, während Jorge sich mehr auf die Semantik konzentriert.
Nazar Merza
8
Ich mag die Aussage vor Ihrer angegebenen Antwort:Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
S1r-Lanzelot
433

Eine abstrakte Klasse kann einen gemeinsamen Status oder eine gemeinsame Funktionalität haben. Eine Schnittstelle ist nur ein Versprechen, den Status oder die Funktionalität bereitzustellen. Eine gute abstrakte Klasse reduziert die Menge an Code, die neu geschrieben werden muss, da die Funktionalität oder der Status gemeinsam genutzt werden können. Die Schnittstelle verfügt über keine definierten Informationen, die gemeinsam genutzt werden sollen

Alex
quelle
65
Dies ist für mich die beste Antwort hier und es ist eine Schande, dass es nicht höher gewählt wurde. Ja, es gibt philosophische Unterschiede zwischen den beiden Konzepten, aber der Kernpunkt ist, dass abstrakte Klassen sicherstellen, dass alle Nachkommen Funktionalität / Status gemeinsam haben, wobei eine Schnittstelle nur eine gemeinsame Bindung gewährleistet.
Drharris
3
Beispielsweise wird eine abstrakte Basisklasse für das Entwurfsmuster der Vorlagenmethode verwendet , während eine Schnittstelle für das Entwurfsmuster der Strategie verwendet wird.
Raedwald
1
Ich denke, Jorges Zusammenfassung erklärt die Hauptursache für die Existenz beider, während Alex 'Antwort der Unterschied in den Ergebnissen ist. Ich wünschte, ich könnte beide als die richtigen Antworten markieren, aber ich bevorzuge immer noch Jorges Antwort.
Chirantan
Und hier ist ein Beispiel mit Code.
Shaijut
Für mich bedeutet dies: "Eine gute abstrakte Klasse reduziert die Menge an Code, die neu geschrieben werden muss, da die Funktionalität oder der Status gemeinsam genutzt werden können." Aussage ist der Kern der Antwort.
Div Tiwari
82

Persönlich muss ich fast nie abstrakte Klassen schreiben.

Meistens sehe ich, dass abstrakte Klassen (falsch) verwendet werden, weil der Autor der abstrakten Klasse das Muster "Vorlagenmethode" verwendet.

Das Problem mit der "Template-Methode" ist, dass sie fast immer etwas neu ist - die "abgeleitete" Klasse kennt nicht nur die "abstrakte" Methode ihrer Basisklasse, die sie implementiert, sondern auch die öffentlichen Methoden der Basisklasse , obwohl es meistens nicht nötig ist, sie anzurufen.

(Zu stark vereinfacht) Beispiel:

abstract class QuickSorter
{
    public void Sort(object[] items)
    {
        // implementation code that somewhere along the way calls:
        bool less = compare(x,y);
        // ... more implementation code
    }
    abstract bool compare(object lhs, object rhs);
}

Hier hat der Autor dieser Klasse einen generischen Algorithmus geschrieben und beabsichtigt, dass die Benutzer ihn verwenden, indem sie ihn "spezialisieren", indem sie ihre eigenen "Hooks" bereitstellen - in diesem Fall eine "Vergleichs" -Methode.

Die beabsichtigte Verwendung ist also ungefähr so:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}

Das Problem dabei ist, dass Sie zwei Konzepte übermäßig miteinander verbunden haben:

  1. Eine Möglichkeit, zwei Elemente zu vergleichen (welches Element sollte zuerst verwendet werden)
  2. Eine Methode zum Sortieren von Elementen (z. B. Quicksortierung oder Zusammenführungssortierung usw.)

Im obigen Code kann der Autor der "compare" -Methode theoretisch die "Sort" -Methode der Oberklasse erneut aufrufen ... obwohl er dies in der Praxis niemals tun möchte oder muss.

Der Preis, den Sie für diese nicht benötigte Kopplung zahlen, ist, dass es schwierig ist, die Oberklasse zu ändern, und in den meisten OO-Sprachen ist es unmöglich, sie zur Laufzeit zu ändern.

Die alternative Methode besteht darin, stattdessen das Entwurfsmuster "Strategie" zu verwenden:

interface IComparator
{
    bool compare(object lhs, object rhs);
}

class QuickSorter
{
    private readonly IComparator comparator;
    public QuickSorter(IComparator comparator)
    {
        this.comparator = comparator;
    }

    public void Sort(object[] items)
    {
        // usual code but call comparator.Compare();
    }
}

class NameComparator : IComparator
{
    bool compare(object lhs, object rhs)
    {
        // same code as before;
    }
}

Beachten Sie jetzt: Wir haben nur Schnittstellen und konkrete Implementierungen dieser Schnittstellen. In der Praxis brauchen Sie eigentlich nichts anderes, um ein OO-Design auf hohem Niveau zu erstellen.

Um die Tatsache zu "verbergen", dass wir die "Sortierung von Namen" mithilfe einer "QuickSort" -Klasse und eines "NameComparator" implementiert haben, schreiben wir möglicherweise noch irgendwo eine Factory-Methode:

ISorter CreateNameSorter()
{
    return new QuickSorter(new NameComparator());
}

Jedes Mal, wenn Sie eine abstrakte Klasse haben, können Sie dies tun ... selbst wenn eine natürliche Wiedereintrittsbeziehung zwischen der Basisklasse und der abgeleiteten Klasse besteht, lohnt es sich normalerweise, sie explizit zu machen.

Ein letzter Gedanke: Alles, was wir oben getan haben, ist, eine "NameSorting" -Funktion mit einer "QuickSort" -Funktion und einer "NameComparison" -Funktion zu "komponieren". In einer funktionalen Programmiersprache wird dieser Programmierstil noch natürlicher. mit weniger Code.

Paul Hollingsworth
quelle
5
Nur weil Sie abstrakte Klassen oder Muster für Vorlagenmethoden verwenden können, müssen Sie diese nicht vermeiden. Strategiemuster ist ein anderes Muster für eine andere Situation als in diesem Beispiel, aber es gibt viele Beispiele, bei denen ein Vorlagenmuster viel besser geeignet ist als Strategie.
Jorge Córdoba
5
Nach meiner Erfahrung stoße ich nie auf sie (Situationen, in denen die Vorlagenmethode vorzuziehen ist) ... oder sowieso selten. Und das ist alles, was "abstrakt" ist - Sprachunterstützung für das Entwurfsmuster "Vorlagenmethode".
Paul Hollingsworth
Ok, ich habe es einmal für ein Expertensystem verwendet, bei dem der Prozess ungefähr so ​​war: 1. FillTheParameters, 2. Machen Sie das Vektorprodukt zwischen ihnen, 3. Berechnen Sie für jedes Paar das Ergebnis, 4. Verbinden Sie die Ergebnisse mit den Schritten 1 und 3 wo delegiert und 2 und 4 in der Basisklasse implementiert.
Jorge Córdoba
10
Ich finde fast jede Verwendung von abstrakten Klassen schwerer zu verstehen. Das Denken in Boxen, die miteinander kommunizieren, anstatt in Vererbungsbeziehungen, ist (für mich) einfacher ... Aber ich stimme auch zu, dass die aktuellen OO-Sprachen zu viel Boilerplate erzwingen ... Funktional wird der Weg sein, über OO
Paul Hollingsworth hinwegzugehen
4
Das Beispiel des Missbrauchs ist ziemlich trivial. Es läuft selten auf eine so schöne, abgespeckte Funktionalität wie Vergleichen hinaus. Viel häufiger sind Situationen, in denen es einige Standardfunktionen gibt, die die abgeleiteten Klassen entweder ersetzen oder erweitern (und im letzteren Fall ist es vollkommen gültig, die Basisklassenfunktion aufzurufen). In Ihrem Beispiel gibt es keine Standardfunktionalität, daher ist die Verwendung der abstrakten Klasse nicht gerechtfertigt.
SomeWittyUsername
41

Wenn Sie Java als OOP-Sprache betrachten,

" Schnittstelle bietet keine Methodenimplementierung " ist beim Start von Java 8 nicht mehr gültig. Jetzt bietet Java die Implementierung in der Schnittstelle für Standardmethoden.

In einfachen Worten möchte ich verwenden

Schnittstelle: Um einen Vertrag durch mehrere unabhängige Objekte zu implementieren. Es bietet " HAS A " -Funktion.

abstrakte Klasse: Um dasselbe oder ein unterschiedliches Verhalten zwischen mehreren verwandten Objekten zu implementieren. Es stellt dieBeziehung" IS A " her.

Die Oracle- Website bietet wichtige Unterschiede zwischen interfaceund abstractKlasse.

Verwenden Sie abstrakte Klassen, wenn:

  1. Sie möchten Code für mehrere eng verwandte Klassen freigeben.
  2. Sie erwarten, dass Klassen, die Ihre abstrakte Klasse erweitern, über viele gängige Methoden oder Felder verfügen oder andere Zugriffsmodifikatoren als öffentliche (z. B. geschützte und private) erfordern.
  3. Sie möchten nicht statische oder nicht endgültige Felder deklarieren.

Erwägen Sie die Verwendung von Schnittstellen, wenn:

  1. Sie erwarten, dass nicht verwandte Klassen Ihre Schnittstelle implementieren. Beispielsweise können viele nicht verwandte Objekte eine SerializableSchnittstelle implementieren .
  2. Sie möchten das Verhalten eines bestimmten Datentyps angeben, sind jedoch nicht darüber besorgt, wer sein Verhalten implementiert.
  3. Sie möchten die Mehrfachvererbung des Typs nutzen.

Beispiel:

Abstrakte Klasse ( IS A Relation)

Reader ist eine abstrakte Klasse.

BufferedReader ist einReader

FileReader ist einReader

FileReaderund BufferedReaderwerden für allgemeine Zwecke verwendet: Lesen von Daten, und sie sind durch ReaderKlasse verbunden.

Schnittstelle ( HAT A- Fähigkeit)

Serializable ist eine Schnittstelle.

Angenommen, Ihre Anwendung enthält zwei Klassen, die die SerializableSchnittstelle implementieren

Employee implements Serializable

Game implements Serializable

Hier können Sie keine Beziehung durch die SerializableSchnittstelle zwischen Employeeund herstellen Game, die für verschiedene Zwecke bestimmt sind. Beide sind in der Lage, den Staat zu serialisieren, und der Vergleich endet dort.

Schauen Sie sich diese Beiträge an:

Wie hätte ich den Unterschied zwischen einer Interface- und einer Abstract-Klasse erklären sollen?

Ravindra Babu
quelle
40

OK, ich habe das gerade selbst "grokked" - hier ist es in Laienbegriffen (zögern Sie nicht, mich zu korrigieren, wenn ich falsch liege) - ich weiß, dass dieses Thema oooooold ist, aber eines Tages könnte jemand anderes darüber stolpern ...

Mit abstrakten Klassen können Sie einen Entwurf erstellen und zusätzlich Eigenschaften und Methoden CONSTRUCT (implementieren), die ALLE Nachkommen besitzen sollen.

Über eine Schnittstelle können Sie jedoch nur deklarieren, dass Eigenschaften und / oder Methoden mit einem bestimmten Namen in allen Klassen vorhanden sein sollen, die sie implementieren. Sie geben jedoch nicht an, wie Sie sie implementieren sollen. Eine Klasse kann auch VIELE Schnittstellen implementieren, aber nur EINE abstrakte Klasse erweitern. Ein Interface ist eher ein Architekturwerkzeug auf hoher Ebene (was klarer wird, wenn Sie anfangen, Entwurfsmuster zu erfassen) - ein Abstract hat in beiden Lagern einen Fuß und kann auch einen Teil der Drecksarbeit ausführen.

Warum übereinander verwenden? Ersteres ermöglicht eine konkretere Definition von Nachkommen - letzteres ermöglicht einen größeren Polymorphismus . Dieser letzte Punkt ist für den Endbenutzer / Codierer wichtig, der diese Informationen verwenden kann, um den AP I (Schnittstelle) in einer Vielzahl von Kombinationen / Formen zu implementieren , um seinen Anforderungen zu entsprechen.

Ich denke , dass dies die „Glühbirne“ Moment für mich war - man denke über Schnittstellen weniger von der Autoren perpective und mehr von dem eines Coder später in der Kette kommen , die Umsetzung eines Projekts ist das Hinzufügen oder Verlängerung eine API.

Sunwukung
quelle
Um darauf aufzubauen: Ein Objekt, das eine Schnittstelle implementiert, nimmt seinen TYP an. Das ist entscheidend. Sie können also verschiedene Variationen der Schnittstelle an eine Klasse übergeben, aber auf sie (und ihre Methoden) mit dem Typnamen der Schnittstelle verweisen. Somit entfällt die Notwendigkeit eines Schalters oder einer if / else-Schleife. Probieren Sie dieses Tutorial zu diesem Thema aus - es demonstriert die Verwendung einer Schnittstelle über das Strategiemuster. phpfreaks.com/tutorial/design-patterns---strategy-and-bridge/…
sunwukung
Ich stimme Ihrem Glühbirnen-Moment voll und ganz zu: "API (nterface) in einer Vielzahl von Kombinationen / Formen, um ihren Bedürfnissen zu entsprechen"! Sehr sehr guter Punkt zu machen.
Trevor Boyd Smith
38

Meine zwei Cent:

Eine Schnittstelle definiert grundsätzlich einen Vertrag, den jede implementierende Klasse einhalten muss (Implementierung der Schnittstellenmitglieder). Es enthält keinen Code.

Andererseits kann eine abstrakte Klasse Code enthalten, und es kann einige Methoden geben, die als abstrakt markiert sind und die eine erbende Klasse implementieren muss.

Die seltenen Situationen, in denen ich abstrakte Klassen verwendet habe, sind, wenn ich einige Standardfunktionen habe, bei denen die erbende Klasse möglicherweise nicht daran interessiert ist, beispielsweise eine abstrakte Basisklasse zu überschreiben, von der einige spezialisierte Klassen erben.

Beispiel (ein sehr rudimentärer ein!): Eine Basisklasse namens Customer betrachten , die wie abstrakte Methoden haben CalculatePayment(), CalculateRewardPoints()und einige nicht-abstrakte Methoden wie GetName(), SavePaymentDetails().

Spezialisierte Klassen mögen RegularCustomer und GoldCustomererben von der CustomerBasisklasse und implementieren ihre eigene CalculatePayment()und CalculateRewardPoints()Methodenlogik, verwenden jedoch die Methoden GetName()und erneut SavePaymentDetails().

Sie können einer abstrakten Klasse (dh nicht abstrakten Methoden) weitere Funktionen hinzufügen, ohne dass sich dies auf untergeordnete Klassen auswirkt, die eine ältere Version verwenden. Das Hinzufügen von Methoden zu einer Schnittstelle würde sich auf alle Klassen auswirken, die sie implementieren, da sie jetzt die neu hinzugefügten Schnittstellenmitglieder implementieren müssten.

Eine abstrakte Klasse mit allen abstrakten Mitgliedern ähnelt einer Schnittstelle.

PreethaA
quelle
1
+1 für "Sie können einer abstrakten Klasse (dh nicht abstrakten Methoden) mehr Funktionen hinzufügen, ohne untergeordnete Klassen zu beeinflussen, die eine ältere Version verwenden. Das Hinzufügen von Methoden zu einer Schnittstelle würde sich auf alle Klassen auswirken, die sie implementieren, wie sie jetzt implementiert werden müssten die neu hinzugefügten Schnittstellenmitglieder. "
Div Tiwari
Schnittstellen können "Standard" -Methoden haben, daher ist es eine falsche Idee, keine Methode in Schnittstellen zu implementieren. "Eltern-zu-Kind" IS-A-Beziehung ist hier der Schlüssel. Außerdem "Gemeinsame Attribute" vs. "Gemeinsame Eigenschaften". zB Hund iS-A Tier. Aber ein Hund kann auch "
laufen
30

Wann zu tun ist, ist eine sehr einfache Sache, wenn Sie das Konzept klar im Kopf haben.

Abstrakte Klassen können abgeleitet werden, während Schnittstellen implementiert werden können. Es gibt einen Unterschied zwischen den beiden. Wenn Sie eine abstrakte Klasse ableiten, ist die Beziehung zwischen der abgeleiteten Klasse und der Basisklasse 'ist eine' Beziehung. Beispiel: Ein Hund ist ein Tier, ein Schaf ist ein Tier, was bedeutet, dass eine abgeleitete Klasse einige Eigenschaften von der Basisklasse erbt.

Während für die Implementierung von Schnittstellen die Beziehung "kann sein" ist. zB kann ein Hund ein Spionagehund sein. Ein Hund kann ein Zirkushund sein. Ein Hund kann ein Rennhund sein. Das bedeutet, dass Sie bestimmte Methoden implementieren, um etwas zu erwerben.

Ich hoffe ich bin klar.

Aamir
quelle
11

1.Wenn Sie etwas erstellen, das nicht verwandten Klassen allgemeine Funktionen bietet, verwenden Sie eine Schnittstelle.

2.Wenn Sie etwas für Objekte erstellen, die in einer Hierarchie eng miteinander verbunden sind, verwenden Sie eine abstrakte Klasse.

kuttychutty
quelle
7

Ich denke, die prägnanteste Art, es auszudrücken, ist die folgende:

Gemeinsame Eigenschaften => abstrakte Klasse.
Gemeinsame Funktionalität => Schnittstelle.

Und um es weniger prägnant auszudrücken ...

Beispiel für eine abstrakte Klasse:

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

Da Tiere eine gemeinsame Eigenschaft haben - in diesem Fall die Anzahl der Beine - ist es sinnvoll, eine abstrakte Klasse zu erstellen, die diese gemeinsame Eigenschaft enthält. Auf diese Weise können wir auch allgemeinen Code schreiben, der für diese Eigenschaft ausgeführt wird. Zum Beispiel:

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

Schnittstellenbeispiel:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

Beachten Sie hier, dass Vuvuzelas und Cars völlig unterschiedliche Dinge sind, aber gemeinsame Funktionen haben: einen Sound erzeugen. Daher ist hier eine Schnittstelle sinnvoll. Außerdem können Programmierer Dinge, die Geräusche erzeugen, unter einer gemeinsamen Oberfläche zusammenfassen - IMakeSoundin diesem Fall. Mit diesem Design können Sie den folgenden Code schreiben:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

Können Sie sagen, was das ausgeben würde?

Zuletzt können Sie beide kombinieren.

Kombiniertes Beispiel:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

Hier müssen alle BaseAnimaleinen Sound erzeugen, aber wir kennen die Implementierung noch nicht. In einem solchen Fall können wir die Schnittstellenimplementierung abstrahieren und ihre Implementierung an ihre Unterklassen delegieren.

Ein letzter Punkt: Erinnern Sie sich daran, wie wir im Beispiel der abstrakten Klasse die gemeinsamen Eigenschaften verschiedener Objekte bearbeiten und im Beispiel der Benutzeroberfläche die gemeinsame Funktionalität verschiedener Objekte aufrufen konnten? In diesem letzten Beispiel könnten wir beides tun.

Domn Werner
quelle
7

Wann ist eine abstrakte Klasse der Schnittstelle vorzuziehen?

  1. Wenn eine Basisklasse während der gesamten Laufzeit eines Programms / Projekts aktualisiert werden soll, ist es am besten, zuzulassen, dass die Basisklasse eine abstrakte Klasse ist
  2. Wenn Sie versuchen, ein Backbone für Objekte zu erstellen, die in einer Hierarchie eng miteinander verbunden sind, ist es äußerst vorteilhaft, eine abstrakte Klasse zu verwenden

Wann sollte eine Schnittstelle der abstrakten Klasse vorgezogen werden?

  1. Wenn es sich nicht um ein massives hierarchisches Framework handelt, sind Schnittstellen eine gute Wahl
  2. Da Mehrfachvererbung bei abstrakten Klassen nicht unterstützt wird (Diamantproblem), können Schnittstellen den Tag retten
Satya
quelle
Warum haben Sie gedacht, dass eine fast zehn Jahre alte Frage eine 22. Antwort benötigt?
Jonrsharpe
3
Dieselbe Art des Denkens, die mich dazu brachte, nach einer einfachen Antwort auf die Frage zu suchen.
Satya
1
FWIW, ich liebe diese Antwort wirklich.
Brent Rittenhouse
6

Klassen erben möglicherweise nur von einer Basisklasse. Wenn Sie also abstrakte Klassen verwenden möchten, um einer Gruppe von Klassen Polymorphismus zu verleihen, müssen sie alle von dieser Klasse erben. Abstrakte Klassen können auch Mitglieder bereitstellen, die bereits implementiert wurden. Daher können Sie mit einer abstrakten Klasse eine bestimmte Menge identischer Funktionen sicherstellen, jedoch nicht mit einer Schnittstelle.

Im Folgenden finden Sie einige Empfehlungen, die Ihnen bei der Entscheidung helfen sollen, ob Sie eine Schnittstelle oder eine abstrakte Klasse verwenden, um Polymorphismus für Ihre Komponenten bereitzustellen.

  • Wenn Sie mehrere Versionen Ihrer Komponente erstellen möchten, erstellen Sie eine abstrakte Klasse. Abstrakte Klassen bieten eine einfache Möglichkeit, Ihre Komponenten zu versionieren. Durch die Aktualisierung der Basisklasse werden alle ererbenden Klassen mit der Änderung automatisch aktualisiert. Schnittstellen hingegen können nach dieser Erstellung nicht mehr geändert werden. Wenn eine neue Version einer Schnittstelle erforderlich ist, müssen Sie eine ganz neue Schnittstelle erstellen.
  • Verwenden Sie eine Schnittstelle, wenn die von Ihnen erstellte Funktionalität für eine Vielzahl unterschiedlicher Objekte nützlich ist. Abstrakte Klassen sollten hauptsächlich für Objekte verwendet werden, die eng miteinander verbunden sind, während Schnittstellen am besten geeignet sind, um nicht verwandten Klassen gemeinsame Funktionen bereitzustellen.
  • Verwenden Sie Schnittstellen, wenn Sie kleine, präzise Funktionen entwerfen. Wenn Sie große Funktionseinheiten entwerfen, verwenden Sie eine abstrakte Klasse.
  • Wenn Sie eine gemeinsame, implementierte Funktionalität für alle Implementierungen Ihrer Komponente bereitstellen möchten, verwenden Sie eine abstrakte Klasse. Mit abstrakten Klassen können Sie Ihre Klasse teilweise implementieren, während Schnittstellen keine Implementierung für Mitglieder enthalten.

Kopiert von:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx

Mazhar
quelle
In UML steht nichts der Vererbung mehrerer Klassen entgegen. Die Mehrfachvererbung wird durch eine Programmiersprache bestimmt, nicht durch UML. Beispielsweise ist die Vererbung mehrerer Klassen in Java und C # nicht zulässig, in C ++ jedoch.
BobRodes
@ BobRodes: Es gibt eine Reihe von Funktionen, die objektorientierte Frameworks in verschiedenen Kombinationen bereitstellen können, jedoch nicht in allen Kombinationen. Die verallgemeinerte Mehrfachvererbung schließt einige andere nützliche Kombinationen von Merkmalen aus, einschließlich der Möglichkeit, einen Verweis direkt auf einen übergeordneten Typ der tatsächlichen Instanz oder einen von ihm unterstützten Schnittstellentyp zu übertragen, und die Möglichkeit, Basistypen und abgeleitete Typen unabhängig zu kompilieren und zur Laufzeit zu verbinden.
Supercat
@supercat Ihre ist eine gute Erklärung für einige der Probleme, die sich aus der Verwendung der Mehrfachvererbung ergeben. Dennoch gibt es in UML nichts, was die Vererbung mehrerer Klassen in einem Diagramm ausschließt. Ich antwortete auf die obigen Fragen "Klassen dürfen nur von einer Basisklasse erben ...", was nicht ganz so ist.
BobRodes
@ BobRodes: Die Frage wurde mit Java markiert. Java enthält die angegebenen Funktionen und ist daher auf Formen der Mehrfachvererbung beschränkt, die keinen "tödlichen Diamanten" erzeugen können (obwohl die Art und Weise, wie sie Standardschnittstellenimplementierungen implementiert haben, den tödlichen Diamanten möglich macht).
Supercat
@ Supercat Oh, ok. Normalerweise schaue ich mir keine Java-Tags an, also dachte ich zum Zeitpunkt des Schreibens zumindest, ich würde eine UML-Antwort kommentieren. In jedem Fall stimme ich Ihrem Kommentar zu.
BobRodes
3

Erwägen Sie die Verwendung abstrakter Klassen, wenn eine dieser Aussagen auf Ihre Situation zutrifft:

  1. Sie möchten Code für mehrere eng verwandte Klassen freigeben.
  2. Sie erwarten, dass Klassen, die Ihre abstrakte Klasse erweitern, viele gängige Methoden oder Felder haben oder andere Zugriffsmodifikatoren als öffentliche (wie geschützte und private) erfordern.
  3. Sie möchten nicht statische oder nicht endgültige Felder deklarieren. Auf diese Weise können Sie Methoden definieren, die auf den Status des Objekts zugreifen und diesen ändern können, zu dem sie gehören.

Erwägen Sie die Verwendung von Schnittstellen, wenn eine dieser Aussagen auf Ihre Situation zutrifft:

  1. Sie erwarten, dass nicht verwandte Klassen Ihre Schnittstelle implementieren. Beispielsweise werden die Schnittstellen Comparable und Cloneable von vielen nicht verwandten Klassen implementiert.
  2. Sie möchten das Verhalten eines bestimmten Datentyps angeben, sind jedoch nicht darüber besorgt, wer sein Verhalten implementiert.
  3. Sie möchten mehrere Vererbungen nutzen.

Quelle

Abhijeet Ashok Muneshwar
quelle
2

Die Antworten variieren zwischen den Sprachen. In Java kann eine Klasse beispielsweise mehrere Schnittstellen implementieren (von diesen erben), aber nur von einer abstrakten Klasse erben. Schnittstellen bieten Ihnen also mehr Flexibilität. Dies gilt jedoch nicht für C ++.

Nick Fortescue
quelle
2

Für mich würde ich in vielen Fällen mit Schnittstellen gehen. In einigen Fällen bevorzuge ich jedoch abstrakte Klassen.

Klassen in OO beziehen sich im Allgemeinen auf die Implementierung. Ich verwende abstrakte Klassen, wenn ich den untergeordneten Elementen, die ich mit Schnittstellen verwende, einige Implementierungsdetails aufzwingen möchte.

Abstrakte Klassen sind natürlich nicht nur nützlich, um die Implementierung zu erzwingen, sondern auch, um einige spezifische Details zwischen vielen verwandten Klassen auszutauschen.

La VloZ Merrill
quelle
1

Verwenden Sie eine abstrakte Klasse, wenn Sie einige grundlegende Implementierungen bereitstellen möchten.

Sebastian
quelle
1
Danke Sebastian. Aber was ist, wenn ich keine grundlegende Implementierung benötige? Werden eine abstrakte Klasse und Schnittstelle dann nicht gleich sein, wenn dies der einzige Unterschied zwischen ihnen ist? Warum gibt es einen Unterschied?
Chirantan
1
Weil einige Sprachen keine Schnittstellen haben - C ++.
jmucchiello
1

In Java können Sie von einer (abstrakten) Klasse erben, um Funktionen bereitzustellen, und Sie können viele Schnittstellen implementieren, um die Funktionalität sicherzustellen

Peter Miehle
quelle
kleiner Hinweis: Wenn Sie von einer abstrakten Klasse und einer Schnittstelle erben möchten, stellen Sie sicher, dass die abstrakte Klasse die Schnittstelle implementiert
Andreas Niedermair
1

Rein auf der Grundlage der Vererbung würden Sie eine Zusammenfassung verwenden, in der Sie eindeutig nachkommende, abstrakte Beziehungen definieren (dh Tier-> Katze) und / oder die Vererbung von virtuellen oder nicht öffentlichen Eigenschaften, insbesondere des gemeinsam genutzten Zustands (die Schnittstellen nicht unterstützen können), erfordern ).

Sie sollten versuchen, die Komposition (über Abhängigkeitsinjektion) der Vererbung vorzuziehen, wo dies möglich ist, und beachten, dass Schnittstellen als Verträge Unit-Tests, die Trennung von Bedenken und (sprachlich variierende) Mehrfachvererbung auf eine Weise unterstützen, die Abstracts nicht können.

Annakata
quelle
1

Ein interessanter Ort, an dem Schnittstellen besser abschneiden als abstrakte Klassen, ist, wenn Sie einer Gruppe von (verwandten oder nicht verwandten) Objekten zusätzliche Funktionen hinzufügen müssen. Wenn Sie ihnen keine abstrakte Basisklasse geben können (z. B. sealedwenn sie übergeordnet sind oder bereits haben), können Sie ihnen stattdessen eine Dummy-Schnittstelle (leer) geben und dann einfach Erweiterungsmethoden für diese Schnittstelle schreiben.

Dmitri Nesteruk
quelle
0

Dies kann ein sehr schwieriger Anruf sein ...

Ein Hinweis, den ich geben kann: Ein Objekt kann viele Schnittstellen implementieren, während ein Objekt nur eine Basisklasse erben kann (in einer modernen OO-Sprache wie c # weiß ich, dass C ++ mehrere Vererbungen hat - aber ist das nicht verpönt?)

Gittergewebe
quelle
Durch mehrfache Vererbung können Mixins scheinbar nahtlos implementiert werden. Gut geschriebene Mixins sind ein Kinderspiel, aber sehr schwer zu bekommen und schwer zu schreiben, ohne irgendwo zu kurz zu kommen. Mixins sind insgesamt ziemlich cool, obwohl IMO.
Martijn Laarman
Eigentlich habe ich nicht, Mehrfachvererbung ist in der Tat ein sicherer Debattenauslöser zwischen uns Freaks. Ich sehe absolut keinen Grund, abzustimmen. Tatsächlich habe ich Ihre Antwort positiv bewertet.
Martijn Laarman
Der einzige Punkt, den ich ansprechen wollte, ist, dass auch Möglichkeiten zum Mischen in Sprachen mit einfacher Vererbung möglich sind (C #, PHP, Javascript), aber durch hackiges Verhalten oder klebrige Syntax. Ich liebe Mixins, wenn sie arbeiten, aber ich bin immer noch unentschlossen, ob ich von Mehrfachvererbung komme oder nicht.
Martijn Laarman
Diese Antwort ist eher ein syntaktischer Unterschied als ein Designunterschied. Ich denke, er bittet um einen Designunterschied
Pramod Setlur
0

Eine abstrakte Klasse kann Implementierungen haben.

Eine Schnittstelle hat keine Implementierungen, sondern definiert lediglich eine Art Vertrag.

Es kann auch einige sprachabhängige Unterschiede geben: Beispielsweise hat C # keine Mehrfachvererbung, aber mehrere Schnittstellen können in einer Klasse implementiert werden.

Gerrie Schenck
quelle
Wenn Sie "eine Art Vertrag" sagen, meinen Sie das wie bei Webdiensten?
Chirantan
Technisch gesehen funktionieren Webdienste nicht mit Schnittstellen. Mit Vertrag meine ich, dass der Benutzer eines Objekts weiß, welche Methoden auf diesem Objekt vorhanden sind. Beispielsweise verfügt eine Schnittstellen-IMouse über eine Verschiebungsmethode sowie ein Ereignis mit der linken und rechten Maustaste.
Gerrie Schenck
-1

Grundlegende Daumenregel lautet: Verwenden Sie für "Nomen" die abstrakte Klasse und für "Verben" die Schnittstelle

ZB: carist eine abstrakte Klasse und drivewir können sie zu einer Schnittstelle machen.

GANI
quelle
5
Das macht keinen Sinn, wir können auch die Funktionalität des driveAutos einbauen - das ist eine abstrakte Klasse.
Arslan Ali