522 Stimmen

Wann sollte man eine Schnittstelle anstelle einer abstrakten Klasse verwenden und umgekehrt?

Dies könnte eine allgemeine OOP-Frage sein. Ich wollte einen generischen Vergleich zwischen einer Schnittstelle und einer abstrakten Klasse auf der Grundlage ihrer Verwendung anstellen.

Wann würde man eine Schnittstelle und wann eine abstrakte Klasse verwenden wollen? ?

538voto

Jorge Córdoba Punkte 49057

Darüber habe ich einen Artikel geschrieben:

Abstrakte Klassen und Schnittstellen

Zusammenfassend:

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

Wenn wir über eine Schnittstelle sprechen und Fähigkeiten definieren, die wir zu liefern versprechen, sprechen wir über einen Vertrag über was das Objekt tun kann.

512voto

Alex Punkte 32100

Eine abstrakte Klasse kann einen gemeinsamen Zustand oder eine gemeinsame Funktionalität haben. Eine Schnittstelle ist nur ein Versprechen, den Zustand oder die Funktionalität bereitzustellen. Eine gute abstrakte Klasse reduziert die Menge an Code, die neu geschrieben werden muss, da ihre Funktionalität oder ihr Zustand gemeinsam genutzt werden kann. Die Schnittstelle hat keine definierten Informationen, die gemeinsam genutzt werden können.

85voto

Paul Hollingsworth Punkte 12494

Ich persönlich habe fast nie das Bedürfnis, abstrakte Klassen zu schreiben.

Wenn ich sehe, dass abstrakte Klassen (falsch) verwendet werden, liegt das meist daran, dass der Autor der abstrakten Klasse das Muster "Schablonenmethode" verwendet.

Das Problem mit der "Schablonenmethode" ist, dass sie fast immer etwas reentrant ist - die "abgeleitete" Klasse weiß nicht nur von der "abstrakten" Methode ihrer Basisklasse, die sie implementiert, sondern auch von den öffentlichen Methoden der Basisklasse, obwohl sie diese meistens nicht aufrufen muss.

(Übermäßig vereinfachtes) 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 also einen allgemeinen Algorithmus geschrieben und möchte, dass andere ihn verwenden, indem sie ihn "spezialisieren", indem sie ihre eigenen "Haken" bereitstellen - in diesem Fall eine "Vergleichsmethode".

Der Verwendungszweck ist also etwa so:

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

Das Problem dabei ist, dass Sie zwei Konzepte in unzulässiger Weise miteinander verknüpft haben:

  1. Eine Möglichkeit, zwei Gegenstände zu vergleichen (welcher Gegenstand sollte zuerst gehen)
  2. Eine Methode zum Sortieren von Elementen (z. B. Schnellsortierung vs. Mischsortierung usw.)

Im obigen Code kann der Autor der "compare"-Methode theoretisch reentrant zurück in die "Sort"-Methode der Superklasse aufrufen... auch wenn sie dies in der Praxis nie tun wollen oder müssen.

Der Preis, den Sie für diese unnötige 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;
    }
}

Also merken Sie sich das jetzt: Alles, was wir haben, sind Schnittstellen und konkrete Implementierungen dieser Schnittstellen. In der Praxis braucht man eigentlich nichts anderes, um ein OO-Design auf hohem Niveau zu erstellen.

Um die Tatsache zu "verstecken", dass wir die "Sortierung von Namen" durch die Verwendung einer "QuickSort"-Klasse und eines "NameComparator" implementiert haben, könnten wir noch irgendwo eine Fabrikmethode schreiben:

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

Jede wenn Sie eine abstrakte Klasse haben, können Sie dies tun... selbst wenn es eine natürliche Wiedereingliederungsbeziehung zwischen der Basis- und der abgeleiteten Klasse gibt, lohnt es sich in der Regel, diese explizit zu machen.

Ein letzter Gedanke: Alles, was wir oben getan haben, ist, eine "NameSorting"-Funktion aus einer "QuickSort"-Funktion und einer "NameComparison"-Funktion zu "komponieren"... in einer funktionalen Programmiersprache wird diese Art der Programmierung noch natürlicher, mit weniger Code.

76voto

Ravindra babu Punkte 45577

Wenn Sie Java als OOP-Sprache betrachten,

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

Einfach ausgedrückt, möchte ich Folgendes verwenden

Schnittstelle: Um einen Vertrag durch mehrere nicht miteinander verbundene Objekte zu implementieren. Es bietet " HAT A Fähigkeit".

abstrakte Klasse: Um dasselbe oder ein unterschiedliches Verhalten bei mehreren verwandten Objekten zu implementieren. Es etabliert " IS A Beziehung".

Oracle Website bietet wichtige Unterschiede zwischen interface y abstract Klasse.

Erwägen Sie die Verwendung abstrakter Klassen wenn :

  1. Sie möchten den Code für mehrere eng miteinander verbundene Klassen gemeinsam nutzen.
  2. Sie erwarten, dass Klassen, die Ihre abstrakte Klasse erweitern, viele gemeinsame Methoden oder Felder haben oder andere Zugriffsmodifikatoren als public (wie protected und private) benötigen.
  3. Sie wollen nicht-statische oder nicht-finale Felder deklarieren.

Erwägen Sie die Verwendung von Schnittstellen wenn :

  1. Sie erwarten, dass nicht verwandte Klassen Ihre Schnittstelle implementieren würden. Zum Beispiel können viele nicht verwandte Objekte implementieren Serializable Schnittstelle.
  2. Sie möchten das Verhalten eines bestimmten Datentyps festlegen, sich aber nicht darum kümmern, wer das Verhalten implementiert.
  3. Sie möchten die Vorteile der Mehrfachvererbung von Typen nutzen.

Abstrakte Klasse ( IS A Beziehung)

Leser ist eine abstrakte Klasse.

BufferedReader ist eine Reader

FileReader ist eine Reader

FileReader y BufferedReader werden für einen gemeinsamen Zweck verwendet: das Lesen von Daten, und sie sind verbunden durch Reader Klasse.

Schnittstelle ( HAT A Fähigkeit )

Serialisierbar ist eine Schnittstelle.

Angenommen, Sie haben zwei Klassen in Ihrer Anwendung, die Folgendes implementieren Serializable Schnittstelle

Employee implements Serializable

Game implements Serializable

Hier können Sie keine Beziehung herstellen durch Serializable Schnittstelle zwischen Employee y Game , die für unterschiedliche Zwecke bestimmt sind. Beide sind in der Lage, den Zustand zu serialisieren, und der Vergleich endet hier.

Werfen Sie einen Blick auf diese Beiträge:

Wie hätte ich den Unterschied zwischen einer Schnittstelle und einer abstrakten Klasse erklären sollen?

49voto

PreethaA Punkte 865

Meine Meinung dazu:

Eine Schnittstelle definiert im Grunde einen Vertrag, an den sich jede implementierende Klasse halten muss (Implementierung der Schnittstellenmitglieder). Sie enthält keinen Code.

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

Die seltenen Situationen, in denen ich abstrakte Klassen verwendet habe, sind, wenn ich eine Standardfunktionalität habe, die die vererbende Klasse nicht überschreiben möchte, z. B. in einer abstrakten Basisklasse, von der einige spezialisierte Klassen erben.

Beispiel (ein sehr rudimentäres!): Betrachten wir eine Basisklasse namens Customer, die abstrakte Methoden hat wie CalculatePayment() , CalculateRewardPoints() und einige nicht-abstrakte Methoden wie GetName() , SavePaymentDetails() .

Spezialisierte Klassen wie RegularCustomer et GoldCustomer erbt von der Customer Basisklasse und implementieren ihre eigene CalculatePayment() y CalculateRewardPoints() Methodenlogik, sondern die Wiederverwendung der GetName() y SavePaymentDetails() Methoden.

Sie können einer abstrakten Klasse (d. h. nicht abstrakten Methoden) mehr Funktionalität hinzufügen, ohne dass dies Auswirkungen auf untergeordnete Klassen hat, die eine ältere Version verwenden. Das Hinzufügen von Methoden zu einer Schnittstelle hingegen würde sich auf alle Klassen auswirken, die diese implementieren, da sie nun die neu hinzugefügten Schnittstellenmitglieder implementieren müssen.

Eine abstrakte Klasse mit allen abstrakten Mitgliedern wäre ähnlich wie eine Schnittstelle.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X