Frage

Wann sollte ich eine Schnittstelle und Wann soll ich eine Basisklasse?

Sollte es immer eine Schnittstelle, wenn ich nicht wollen, um tatsächlich definieren Sie eine Basis-Implementierung der Methoden?

Wenn ich ein Hund und Katze.Klasse.Warum möchte ich zu implementieren IPet statt PetBase?Ich kann verstehen, dass Schnittstellen für ISheds oder IBarks (IMakesNoise?), weil diejenigen, die platziert werden kann auf ein Haustier von pet-basis, aber ich verstehe nicht, was für ein generisches Haustier.

War es hilfreich?

Lösung

Nehmen wir Ihr Beispiel für einen Hund und eine Katzenklasse und veranschaulichen wir mit C#:

Sowohl ein Hund als auch eine Katze sind Tiere, insbesondere Vierfach -Säugetiere (Tiere sind zu allgemein). Nehmen wir an, Sie haben für beide ein abstraktes Klassensäugetier:

public abstract class Mammal

Diese Basisklasse wird wahrscheinlich Standardmethoden haben wie:

  • Einspeisung
  • Kamerad

Alle von ihnen sind Verhaltensweisen, die mehr oder weniger die gleiche Umsetzung zwischen beiden Arten haben. Um dies zu definieren, werden Sie:

public class Dog : Mammal
public class Cat : Mammal

Nehmen wir nun an, es gibt andere Säugetiere, die wir normalerweise in einem Zoo sehen werden:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Dies ist weiterhin gültig, da im Kern der Funktionalität Feed() und Mate() wird immer noch gleich sein.

Giraffen, Rhinoceros und Flusspferd sind jedoch nicht genau Tiere, aus denen Sie Haustiere machen können. Hier wird eine Schnittstelle nützlich sein:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Die Umsetzung für den obigen Vertrag ist zwischen Katze und Hund nicht gleich; Es wird eine schlechte Idee sein, ihre Implementierungen in eine abstrakte Klasse in die Erbe zu bringen.

Ihre Hund- und Katzendefinitionen sollten jetzt so aussehen:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Theoretisch können Sie sie von einer höheren Basisklasse überschreiben, aber im Wesentlichen können Sie mit einer Schnittstelle nur die Dinge in eine Klasse hinzufügen, ohne dass die Vererbung erforderlich ist.

Da Sie normalerweise nur von einer abstrakten Klasse erben können (in den am meisten statisch getippten OO -Sprachen, dh ... Ausnahmen umfassen C ++), können Sie jedoch mehrere Schnittstellen implementieren, sondern können Sie Objekte in einem streng nach Bedarf Basis.

Andere Tipps

Nun, Josh Bloch sagte sich in Effektiver Java 2d:

Bevorzugen Sie Schnittstellen gegenüber abstrakten Klassen

Einige Hauptpunkte:

  • Bestehende Klassen können leicht nachgerüstet werden, um eine neue Schnittstelle zu implementieren. Alles, was Sie tun müssen, ist die erforderlichen Methoden hinzuzufügen, wenn sie noch nicht vorhanden sind, und der Klassenerklärung eine Klausel implementiert.

  • Schnittstellen sind ideal zum Definieren von Mixins. Ein Mixin ist ein Typ, den eine Klasse zusätzlich zu seinem „Primärtyp“ implementieren kann, um zu erklären, dass sie ein optionales Verhalten bietet. Beispielsweise ist vergleichbar eine Mixin -Schnittstelle, mit der eine Klasse erklärt, dass ihre Instanzen in Bezug auf andere gegenseitig vergleichbare Objekte geordnet sind.

  • Schnittstellen ermöglichen die Konstruktion nicht hierarchischer Typ -Frameworks. Typhierarchien sind großartig, um einige Dinge zu organisieren, aber andere Dinge fallen nicht ordentlich in eine starre Hierarchie.

  • Schnittstellen ermöglichen sichere, leistungsstarke Funktionalitätsverbesserungen über die Wrap-per-Klasse-Idiom. Wenn Sie abstrakte Klassen verwenden, um Typen zu definieren, lassen Sie den Programmierer, der Funktionen ohne Alternative, sondern die Vererbung verwenden möchte, die Funktionalität hinzufügen.

Darüber hinaus können Sie die Tugenden von Schnittstellen und abstrakten Klassen kombinieren, indem Sie eine abstrakte Skelett -Implementierungsklasse bereitstellen, die für jede nicht triviale Schnittstelle, die Sie exportieren, entsprechen.

Andererseits sind Schnittstellen sehr schwer zu entwickeln. Wenn Sie einer Schnittstelle eine Methode hinzufügen, werden alle IT -Implementierungen gebrochen.

Ps.: Kaufen Sie das Buch. Es ist viel detaillierter.

Moderner Stil ist, IPET zu definieren und Petbase.

Der Vorteil der Schnittstelle besteht darin, dass anderer Code ihn ohne Bindungen zu einem anderen ausführbaren Code verwenden kann. Ganz "sauber". Auch Schnittstellen können gemischt werden.

Basisklassen sind jedoch nützlich für einfache Implementierungen und gemeinsame Dienstprogramme. Geben Sie also auch eine abstrakte Basisklasse an, um Zeit und Code zu sparen.

Schnittstellen und Basisklassen repräsentieren zwei verschiedene Formen von Beziehungen.

Nachlass (Basisklassen) repräsentieren eine "is-a" -Beziehung. ZB ein Hund oder eine Katze "is-a" Haustier. Diese Beziehung repräsentiert immer die (Single) Zweck der Klasse (in Verbindung mit der "Prinzip der Einzelverantwortung").

Schnittstellen, andererseits darstellen Zusatzfunktionen einer Klasse. Ich würde es als "is" Beziehung nennen, wie in "Foo ist verfügbar ", daher die IDisposable Schnittstelle in C#.

Schnittstellen

  • Definiert den Vertrag zwischen 2 Modulen. Kann keine Implementierung haben.
  • Mit den meisten Sprachen können Sie mehrere Schnittstellen implementieren
  • Das Ändern einer Schnittstelle ist eine Bruchänderung. Alle Implementierungen müssen neu kompiliert/geändert werden.
  • Alle Mitglieder sind öffentlich. Implementierungen müssen alle Mitglieder implementieren.
  • Schnittstellen helfen bei der Entkopplung. Sie können Mock Frameworks verwenden, um etwas hinter einer Schnittstelle zu verspotten
  • Schnittstellen zeigen normalerweise eine Art Verhalten an
  • Schnittstellenimplementierungen werden voneinander entkoppelt / isoliert

Basisklassen

  • Ermöglicht es Ihnen, einige hinzuzufügen Ursprünglich Implementierung, die Sie durch Ableitung kostenlos erhalten
  • Mit Ausnahme von C ++ können Sie nur aus einer Klasse ableiten. Selbst wenn es aus mehreren Klassen aus könnte, ist es normalerweise eine schlechte Idee.
  • Das Ändern der Basisklasse ist relativ einfach. Ableitungen müssen nichts Besonderes tun
  • Basisklassen können geschützte und öffentliche Funktionen deklarieren, auf die durch Ableitungen zugegriffen werden können
  • Abstrakte Basisklassen können nicht leicht wie Schnittstellen verspottet werden
  • Basisklassen geben normalerweise die Typhierarchie an (ist a)
  • Klassenableitungen können von einem Basisverhalten abhängen (haben komplizierte Kenntnisse über die Implementierung von Eltern). Die Dinge können chaotisch sein, wenn Sie die Basisimplementierung für einen Mann ändern und die anderen brechen.

Im Allgemeinen sollten Sie Schnittstellen gegenüber abstrakten Klassen bevorzugen. Ein Grund für die Verwendung einer abstrakten Klasse ist, wenn Sie eine gemeinsame Implementierung unter konkreten Klassen haben. Natürlich sollten Sie weiterhin eine Schnittstelle (IPET) deklarieren und eine abstrakte Klasse (PETBase) implementieren, wenn Sie kleine, unterschiedliche Schnittstellen verwenden, können Sie Multiples verwenden, um die Flexibilität weiter zu verbessern. Schnittstellen ermöglichen die maximale Menge an Flexibilität und Tragbarkeit von Typen über Grenzen hinweg. Wenn Sie Referenzen über Grenzen hinweg übergeben, übergeben Sie immer die Schnittstelle und nicht den Betontyp. Dies ermöglicht das Empfangsende die Bestimmung der konkreten Implementierung und bietet maximale Flexibilität. Dies gilt absolut bei der Programmierung in TDD/BDD -Weise.

Die vierköpfige Bande erklärte in ihrem Buch "weil Erbschaft eine Unterklasse der Details der Umsetzung seines Elternteils ausstellt, wird oft gesagt, dass die Vererbung die Einkapselung bricht". Ich glaube, das ist wahr.

Dies ist ziemlich .NET -spezifisch, aber das Richtlinien des Framework -Designs argumentiert, dass im Allgemeinen Klassen in einem sich entwickelnden Framework mehr Flexibilität verleihen. Sobald eine Schnittstelle versendet wurde, haben Sie nicht die Möglichkeit, sie zu ändern, ohne den Code zu brechen, der diese Schnittstelle verwendet hat. Mit einer Klasse können Sie ihn jedoch ändern und keinen Code brechen, der darauf verlinkt. Solange Sie die richtigen Änderungen vornehmen, darunter das Hinzufügen neuer Funktionen, können Sie Ihren Code erweitern und weiterentwickeln.

Krzysztof Cwalina sagt auf Seite 81:

Im Laufe der drei Versionen des .NET -Frameworks habe ich mit einigen Entwicklern unseres Teams über diese Richtlinie gesprochen. Viele von ihnen, einschließlich derer, die zunächst mit den Richtlinien nicht einverstanden waren, haben gesagt, dass sie es bedauern, eine API als Schnittstelle versendet zu haben. Ich habe noch nicht einmal von einem Fall gehört, in dem jemand es bedauerte, dass er eine Klasse versandt hat.

Davon abgesehen gibt es sicherlich einen Ort für Schnittstellen. Als allgemeine Richtlinie bieten Sie immer eine abstrakte Basisklassenimplementierung einer Schnittstelle, wenn für nichts anderes als Beispiel für eine Möglichkeit zur Implementierung der Schnittstelle. Im besten Fall spart dieser Basisklasse viel Arbeit.

Juan,

Ich denke gerne Schnittstellen als Möglichkeit, eine Klasse zu charakterisieren. Eine bestimmte Hunderassenklasse, sagen ein Yorkshireterrier, ist möglicherweise ein von der Elternhundeklasse abgestammelt, aber es ist auch implementiert, dass Ifurry, isubby und IyippieDog. Die Klasse definiert also, was die Klasse ist, aber die Schnittstelle erzählt uns Dinge darüber.

Der Vorteil davon ist, dass ich zum Beispiel alle iyippiedogs sammeln und in meine Ozeansammlung werfen kann. Jetzt kann ich jetzt über eine bestimmte Reihe von Objekten greifen und diejenigen finden, die die Kriterien erfüllen, die ich betrachte, ohne die Klasse zu genau zu inspizieren.

Ich finde, dass Schnittstellen wirklich einen Teil des öffentlichen Verhaltens einer Klasse definieren sollten. Wenn es das öffentliche Verhalten für alle implementierenden Klassen definiert, muss es normalerweise nicht existieren. Sie sagen mir nichts Nützliches.

Dieser Gedanke entspricht jedoch der Idee, dass jede Klasse eine Schnittstelle haben sollte und Sie der Schnittstelle codieren sollten. Das ist in Ordnung, aber am Ende haben Sie eine Menge eins zu eins zu Klassen und es macht die Dinge verwirrend. Ich verstehe, dass die Idee ist, dass es nichts zu tun hat, und jetzt können Sie die Dinge mit Leichtigkeit austauschen. Ich finde jedoch, dass ich das selten mache. Die meiste Zeit ändere ich nur die vorhandene Klasse und habe genau die gleichen Probleme, die ich immer gemacht habe, wenn sich die öffentliche Schnittstelle dieser Klasse ändern muss, außer dass ich sie jetzt an zwei Orten ändern muss.

Wenn Sie also wie ich denken, würden Sie definitiv sagen, dass Katze und Hund ipettierbar sind. Es ist eine Charakterisierung, die beide entspricht.

Das andere Stück davon ist jedoch die gleiche Basisklasse? Die Frage ist, ob sie weitgehend als dasselbe behandelt werden müssen. Sicher sind sie beide Tiere, aber das passt dazu, wie wir sie zusammen benutzen werden.

Sagen Sie, ich möchte alle Tierklassen sammeln und sie in meinen Arkbehälter stecken.

Oder müssen sie Säugetiere sein? Vielleicht brauchen wir eine Art Cross Animal Melking -Fabrik?

Müssen sie überhaupt miteinander verbunden werden? Ist es genug, nur zu wissen, dass sie beide ipettierbar sind?

Ich spüre oft den Wunsch, eine ganze Klassehierarchie abzuleiten, wenn ich wirklich nur eine Klasse brauche. Ich mache es eines Tages in Erwartung, ich brauche es vielleicht und normalerweise mache ich es nie. Selbst wenn ich das tue, muss ich normalerweise viel tun, um es zu beheben. Das liegt daran, dass die erste Klasse, die ich erstelle, nicht der Hund ist, ich habe nicht so viel Glück, es ist stattdessen der Platypus. Jetzt basiert meine gesamte Klassenhierarchie auf dem bizarren Fall und ich habe viele verschwendete Code.

Vielleicht finden Sie irgendwann auch, dass nicht alle Katzen ipettierbar sind (wie das haarlos). Jetzt können Sie diese Schnittstelle zu allen Ableitungsklassen verschieben, die passen. Sie werden feststellen, dass eine viel weniger brichtliche Veränderung, die plötzliche Katzen nicht mehr von PettableBase abgeleitet werden.

Hier ist der grundlegende und einfache definitonische Schnittstellen- und Basisklassen:

  • Basisklasse = Objektvererbung.
  • Schnittstelle = Funktionaler Vererbung.

Prost

Ich empfehle, nach Möglichkeit Komposition anstelle der Erbschaft zu verwenden. Verwenden Sie Schnittstellen, verwenden Sie jedoch Elementobjekte für die Basisimplementierung. Auf diese Weise können Sie eine Fabrik definieren, die Ihre Objekte konstruiert, um sich auf eine bestimmte Weise zu verhalten. Wenn Sie das Verhalten ändern möchten, erstellen Sie eine neue Werksmethode (oder abstrakte Fabrik), die verschiedene Arten von Sub-Objekten erstellt.

In einigen Fällen können Sie feststellen, dass Ihre primären Objekte überhaupt keine Schnittstellen benötigen, wenn das gesamte veränderliche Verhalten in Helferobjekten definiert ist.

Anstelle von IPET oder PETBASE können Sie möglicherweise ein Haustier mit einem IFURBEHAVIOR -Parameter haben. Der IFURBEHAVIOR -Parameter wird von der Methode "CreateDog () des PetFactory festgelegt. Es ist dieser Parameter, der für die Shed () -Methode erforderlich ist.

Wenn Sie dies tun, finden Sie, dass Ihr Code viel flexibler ist und die meisten Ihrer einfachen Objekte mit sehr grundlegenden systemweiten Verhaltensweisen umgehen.

Ich empfehle dieses Muster auch in Sprachen mit mehreren Inheriten.

Gut erklärt in diesem Java World Artikel

Persönlich neige ich dazu, Schnittstellen zu verwenden, um Schnittstellen zu definieren - dh Teile des Systemdesigns, die angeben, wie etwas zugegriffen werden sollte.

Es ist nicht ungewöhnlich, dass ich eine Klasse haben werde, in der 1 oder mehr Schnittstellen implementiert werden.

Abstrakte Klassen, die ich als Grundlage für etwas anderes verwende.

Das Folgende ist ein Auszug aus dem oben genannten Artikel JavaRld.com Artikel, Autor Tony Stes, 20.04.01


Schnittstelle vs. abstrakte Klasse

Die Auswahl von Schnittstellen und abstrakten Klassen ist kein/oder Aussagen. Wenn Sie Ihr Design ändern müssen, machen Sie es zu einer Schnittstelle. Möglicherweise haben Sie jedoch abstrakte Klassen, die ein Standardverhalten bieten. Abstrakte Klassen sind ausgezeichnete Kandidaten innerhalb von Anwendungsrahmen.

Mit abstrakten Klassen können Sie einige Verhaltensweisen definieren. Sie zwingen Ihre Unterklassen, andere zu liefern. Wenn Sie beispielsweise über ein Anwendungsrahmen verfügen, kann eine abstrakte Klasse Standarddienste wie Ereignis- und Nachrichtenbearbeitung anbieten. Diese Dienste ermöglichen es Ihrer Anwendung, sich an Ihr Anwendungsframework anzuschließen. Es gibt jedoch einige anwendungsspezifische Funktionen, die nur Ihre Anwendung ausführen kann. Diese Funktionalität kann Start- und Herunterfahren umfassen, die häufig anwendungsabhängig sind. Anstatt zu versuchen, dieses Verhalten selbst zu definieren, kann die abstrakte Basisklasse abstrakte Abschalt- und Start -up -Methoden deklarieren. Die Basisklasse weiß, dass sie diese Methoden braucht, aber eine abstrakte Klasse lässt Ihre Klasse zugeben, dass sie nicht weiß, wie diese Aktionen ausgeführt werden können. Es weiß nur, dass es die Handlungen einleiten muss. Wenn es Zeit zum Starten ist, kann die abstrakte Klasse die Startmethode aufrufen. Wenn die Basisklasse diese Methode aufruft, ruft Java die von der untergeordnete Klasse definierte Methode auf.

Viele Entwickler vergessen, dass eine Klasse, die eine abstrakte Methode definiert, diese Methode auch aufrufen kann. Abstrakte Klassen sind eine hervorragende Möglichkeit, geplante Erbschaftshierarchien zu erstellen. Sie sind auch eine gute Wahl für Nicht -Leafklassen in Klassenhierarchien.

Klasse vs. Schnittstelle

Einige sagen, Sie sollten alle Klassen in Bezug auf Schnittstellen definieren, aber ich denke, die Empfehlung scheint ein bisschen extrem zu sein. Ich benutze Schnittstellen, wenn ich sehe, dass sich etwas in meinem Design häufig ändert.

Mit dem Strategiemuster können Sie beispielsweise neue Algorithmen und Prozesse in Ihr Programm tauschen, ohne die Objekte zu ändern, die sie verwenden. Ein Media -Player weiß möglicherweise, wie man CDs, MP3s und WAV -Dateien abspielt. Natürlich möchten Sie diese Wiedergabealgorithmen nicht in den Spieler harten. Das wird es schwierig machen, ein neues Format wie AVI hinzuzufügen. Darüber hinaus wird Ihr Code mit nutzlosen Fallanweisungen übersät. Um Verletzungen zu beleidigen, müssen Sie diese Fallanweisungen jedes Mal aktualisieren, wenn Sie einen neuen Algorithmus hinzufügen. Alles in allem ist dies keine sehr objektorientierte Art und Weise zu programmieren.

Mit dem Strategiemuster können Sie den Algorithmus hinter einem Objekt einfach zusammenfassen. Wenn Sie dies tun, können Sie jederzeit neue Medien-Plug-Ins bereitstellen. Nennen wir die Plug-in-Klasse Mediastrategy. Dieses Objekt hätte eine Methode: PlayStream (Stream S). Um einen neuen Algorithmus hinzuzufügen, erweitern wir einfach unsere Algorithmusklasse. Wenn das Programm nun auf den neuen Medientyp trifft, delegiert es einfach das Spiel des Streams an unsere Medienstrategie. Natürlich benötigen Sie eine Klempnerarbeiten, um die von Ihnen benötigten Algorithmusstrategien ordnungsgemäß zu instanziieren.

Dies ist ein ausgezeichneter Ort, um eine Schnittstelle zu verwenden. Wir haben das Strategiemuster verwendet, das eindeutig einen Ort im Design anzeigt, der sich ändert. Daher sollten Sie die Strategie als Schnittstelle definieren. Sie sollten im Allgemeinen Schnittstellen gegenüber der Vererbung bevorzugen, wenn ein Objekt einen bestimmten Typ hat. In diesem Fall MediaStrategy. Es ist gefährlich, sich auf die Vererbung für die Typidentität zu verlassen. Es sperrt Sie in eine bestimmte Vererbungshierarchie. Java erlaubt kein mehrfacher Vererbung, sodass Sie nichts erweitern können, das Ihnen eine nützliche Implementierung oder mehr Typidentität bietet.

Denken Sie auch daran, nicht in OO (Siehe Blog) und immer Modellobjekte basierend auf dem erforderlichen Verhalten, wenn Sie eine App entwerfen würden, in der das einzige Verhalten, das Sie benötigten Klassen für jedes mögliche Tier der Welt.

Ich habe eine grobe Faustregel

Funktionalität: wahrscheinlich in allen Teilen unterschiedlich sein: Schnittstelle.

Daten und Funktionalität, Teile sind meistens gleich, Teile unterschiedlich: abstrakte Klasse.

Daten und Funktionen, die tatsächlich funktionieren, wenn sie nur mit geringfügigen Änderungen erweitert werden: gewöhnliche (konkrete) Klasse

Daten und Funktionen, keine Änderungen geplant: Gewöhnliche (konkrete) Klasse mit endgültigem Modifikator.

Daten und möglicherweise Funktionen: schreibgeschützt: enum Mitglieder.

Dies ist sehr rau und bereit und überhaupt nicht streng definiert, aber es gibt ein Spektrum von Schnittstellen, in denen alles in Enums geändert werden soll, in denen alles ein bisschen wie eine schreibgeschützte Datei behoben ist.

Schnittstellen sollten klein sein. Wirklich klein. Wenn Sie Ihre Objekte wirklich abbauen, enthalten Ihre Schnittstellen wahrscheinlich nur einige sehr spezifische Methoden und Eigenschaften.

Abstrakte Klassen sind Verknüpfungen. Gibt es Dinge, die alle Ableitungen von Petbase teilen, mit denen Sie einmal codieren und mit denen Sie fertig sind? Wenn ja, dann ist es Zeit für eine abstrakte Klasse.

Abstrakte Klassen begrenzen ebenfalls. Während sie Ihnen eine großartige Abkürzung zur Herstellung von Kinderobjekten geben, kann ein bestimmtes Objekt nur eine abstrakte Klasse implementieren. Oft finde ich dies als Einschränkung abstrakter Klassen, und deshalb verwende ich viele Schnittstellen.

Abstrakte Klassen können mehrere Schnittstellen enthalten. Ihre Petbase -Abstract -Klasse kann IPET (Haustiere haben Eigentümer) und Wüstenstörungen (Haustiere essen oder zumindest sollten sie) implementieren. Petbase wird jedoch wahrscheinlich nicht Imammal implementieren, da nicht alle Haustiere Säugetiere sind und nicht alle Säugetiere Haustiere sind. Sie können eine Säugetierbasis hinzufügen, die Petbase erweitert und Imammal hinzufügen. Fishbase könnte Petbase haben und Ifish hinzufügen. Ifish hätte Iswim und IunderwaterBreather als Schnittstellen.

Ja, mein Beispiel ist für das einfache Beispiel ausführlich überkompliziert, aber das ist Teil der großartigen Sache, wie Schnittstellen und abstrakte Klassen zusammenarbeiten.

Der Fall für Basisklassen über Schnittstellen wurde in den Submain -.NET -Codierungsrichtlinien gut erklärt:

Grundklassen gegen Schnittstellen Ein Schnittstellentyp ist eine teilweise Beschreibung eines Wertes, der möglicherweise von vielen Objekttypen unterstützt wird. Verwenden Sie nach Möglichkeit Basisklassen anstelle von Schnittstellen. Aus der Perspektive der Versioning sind Klassen flexibler als Schnittstellen. Mit einer Klasse können Sie Version 1.0 versenden und dann in Version 2.0 der Klasse eine neue Methode hinzufügen. Solange die Methode nicht abstrakt ist, funktionieren alle vorhandenen abgeleiteten Klassen weiterhin unverändert.

Da Schnittstellen die Implementierungserbranz nicht unterstützen, gilt das für Klassen gilt nicht für Schnittstellen. Das Hinzufügen einer Methode zu einer Schnittstelle entspricht dem Hinzufügen einer abstrakten Methode zu einer Basisklasse. Jede Klasse, die die Schnittstelle implementiert, wird brechen, da die Klasse die neue Methode nicht implementiert. Schnittstellen sind in den folgenden Situationen geeignet:

  1. Mehrere nicht verwandte Klassen wollen das Protokoll unterstützen.
  2. Diese Klassen haben bereits Basisklassen festgelegt (zum Beispiel sind einige Benutzeroberflächen der Benutzeroberfläche (UI) und einige sind XML -Webdienste).
  3. Aggregation ist nicht angemessen oder praktikabel. In allen anderen Situationen ist die Klassenvererbung ein besseres Modell.

Quelle: http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-hould-you-use/

C# ist eine wundervolle Sprache, die in den letzten 14 Jahren gereift und entwickelt wurde. Dies ist ideal für US -Entwickler, da eine reife Sprache uns eine Vielzahl von Sprachmerkmalen bietet, die uns zur Verfügung stehen.

Mit viel Macht wird jedoch viel Verantwortung. Einige dieser Funktionen können missbraucht werden, oder manchmal ist es schwer zu verstehen, warum Sie eine Funktion über eine andere verwenden würden. Im Laufe der Jahre ist eine Funktion, mit der viele Entwickler zu kämpfen haben, wann eine Schnittstelle verwendet oder eine abstrakte Klasse verwendet werden soll. Beide haben Vor- und Nachteile und die richtige Zeit und den richtigen Ort für die Verwendung. Aber wie entscheiden wir uns ???

Beide sorgen für die Wiederverwendung gemeinsamer Funktionen zwischen Typen. Der offensichtlichste Unterschied besteht sofort darin, dass Schnittstellen keine Implementierung für ihre Funktionalität bieten, während abstrakte Klassen es Ihnen ermöglichen, ein „Basis“ oder „Standard“ -Verhalten zu implementieren und dann die Möglichkeit zu haben, dieses Standardverhalten mit den abgeleiteten Klassen zu „überschreiben“, falls erforderlich .

Das ist alles gut und gut und sorgt für eine große Wiederverwendung von Code und hält sich an das trockene Prinzip der Softwareentwicklung. Abstrakte Klassen sind großartig zu verwenden, wenn Sie eine „IS A“ -Beziehung haben.

Zum Beispiel: Ein Golden Retriever "ist ein" Art des Hundes. So ist ein Pudel. Sie können beide bellen, wie alle Hunde können. Möglicherweise möchten Sie jedoch angeben, dass sich der Pudelpark erheblich von der „Standard“ -Hundrinde unterscheidet. Daher könnte es für Sie sinnvoll sein, etwas wie folgt zu implementieren:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Wie Sie sehen können, wäre dies eine großartige Möglichkeit, Ihren Code trocken zu halten und die Implementierung der Basisklassen aufgerufen zu lassen, wenn einer der Typen nur auf die Standardrinde anstelle einer Sonderfall -Implementierung beruhen kann. Die Klassen wie Goldenriever, Boxer, Labor könnten alle die Rinde „Standard“ (Bass -Klasse) ohne Anklage erben, nur weil sie die Abstract -Klasse der Hunde implementieren.

Aber ich bin mir sicher, dass Sie das schon gewusst haben.

Sie sind hier, weil Sie verstehen möchten, warum Sie möglicherweise eine Schnittstelle über eine abstrakte Klasse auswählen oder umgekehrt. Ein Grund, warum Sie möglicherweise eine Schnittstelle über eine abstrakte Klasse auswählen möchten, ist, wenn Sie keine Standardimplementierung haben oder verhindern möchten. Dies liegt normalerweise daran, dass die Typen, die die Schnittstelle implementieren, nicht in einer „IS A“ -Beziehung zusammenhängen. Eigentlich müssen sie überhaupt nicht verwandt sein, außer dass jeder Typ „in der Lage ist“ oder „die Ablagen“ hat, etwas zu tun oder etwas zu haben.

Was bedeutet das zum Teufel? Nun, zum Beispiel: Ein Mensch ist keine Ente ... und eine Ente ist kein Mensch. Ganz schön offensichtlich. Sowohl eine Ente als auch ein Mensch haben jedoch die Fähigkeit zum Schwimmen (da der Mensch in der 1. Klasse seine Schwimmunterricht bestanden hat :)). Da eine Ente kein Mensch oder umgekehrt ist, handelt es sich nicht um eine Realation von „IS A“, sondern eine „Is fähige“ Beziehung, und wir können eine Schnittstelle verwenden, um zu veranschaulichen:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

Mithilfe von Schnittstellen wie dem obigen Code können Sie ein Objekt in eine Methode übergeben, die „in der Lage ist“, etwas zu tun. Es ist dem Code egal, wie es es macht. Alles, was er weiß, ist, dass er die Schwimmmethode für dieses Objekt aufrufen kann und dass Objekt weiß, welches Verhalten bei der Laufzeit basierend auf seinem Typ einnimmt.

Dies hilft Ihrem Code erneut, trocken zu bleiben, damit Sie nicht mehrere Methoden schreiben müssen, die das Objekt aufrufen, um dieselbe Kernfunktion (Showhowhumanswims (menschlich), Showhowduckswims (Ente) usw.) vorzunehmen.

Durch die Verwendung einer Schnittstelle hier können die aufrufenden Methoden keine Sorgen darüber machen, welcher Typ ist oder wie das Verhalten implementiert wird. Es weiß nur, dass jedes Objekt angesichts der Schnittstelle die Schwimmmethode implementiert haben muss, damit es sicher in seinem eigenen Code aufgerufen und das Verhalten der Schwimmmethode in seiner eigenen Klasse behandelt werden kann.

Zusammenfassung:

Meine Hauptstämmeregel ist also eine abstrakte Klasse, wenn Sie eine „Standard“ -Funktionalität für eine Klassenhierarchie oder/und die Klassen oder Typen, mit denen Sie arbeiten, eine „IS A“ -Brelation (z. B. Pudel “ist, implementieren möchten Art des Hundes).

Andererseits verwenden Sie eine Schnittstelle, wenn Sie keine „IS A“ -Beziehung haben, sondern Typen haben, die „die Fähigkeit“ teilen, etwas zu tun oder etwas zu haben (z. "Die Fähigkeit" zu schwimmen).

Ein weiterer Unterschied zwischen abstrakten Klassen und Schnittstellen besteht darin, dass eine Klasse eine zu vielen Schnittstellen implementieren kann, aber eine Klasse kann nur von einer abstrakten Klasse (oder einer beliebigen Klasse) erben. Ja, Sie können Klassen nisten und eine Vererbungshierarchie (die viele Programme haben und haben), aber Sie können zwei Klassen nicht in einer abgeleiteten Klassendefinition erben (diese Regel gilt für C#. In einigen anderen Sprachen können Sie dies normalerweise tun, normalerweise, normalerweise Nur wegen des Mangels an Schnittstellen in diesen Sprachen).

Denken Sie auch daran, dass Sie Schnittstellen verwenden, um das Schnittstellen -Segregationsprinzip (ISP) zu halten. ISP gibt an, dass kein Client gezwungen sein sollte, auf Methoden abhängig zu sein, die es nicht verwendet. Aus diesem Grund sollten sich die Schnittstellen auf bestimmte Aufgaben konzentrieren und sind normalerweise sehr klein (z. B. identisch, ikparabel).

Ein weiterer Tipp ist, wenn Sie kleine, prägnante Funktionen entwickeln, verwenden Sie Schnittstellen. Wenn Sie große funktionale Einheiten entwerfen, verwenden Sie eine abstrakte Klasse.

Hoffe das klärt die Dinge für manche Menschen auf!

Auch wenn Sie sich bessere Beispiele vorstellen oder etwas darauf hinweisen möchten, tun Sie dies bitte in den Kommentaren unten!

Ein wichtiger Unterschied ist, dass Sie nur erben können eines Basisklasse, aber Sie können implementieren viele Schnittstellen. Sie möchten also nur eine Basisklasse verwenden, wenn Sie es sind absolut sicher Dass Sie nicht eine andere Basisklasse erben müssen. Wenn Sie feststellen, dass Ihre Schnittstelle groß wird, sollten Sie sie in ein paar logische Stücke, die unabhängige Funktionen definieren Schnittstelle, die sie alle erbt, um sie zu gruppieren).

Als ich anfing, etwas über objektorientierte Programmierung zu lernen, machte ich den einfachen und wahrscheinlich häufigen Fehler, das Vererbung zu verwenden, um gemeinsames Verhalten zu teilen - selbst wenn dieses Verhalten für die Natur des Objekts nicht wesentlich war.

Um weiter auf einem Beispiel aufzubauen, das in dieser speziellen Frage viel verwendet wird, gibt es viele Von Dingen, die vernünftig sind - Freundinnen, Autos, Fuzzy -Decken ... - also hätte ich vielleicht eine belebbare Klasse gehabt, die dieses gemeinsame Verhalten lieferte, und verschiedene Klassen, die daraus erbten.

Verpelzbar zu sein ist jedoch nicht Teil der Natur dieser Objekte. Es gibt weitaus wichtigere Konzepte, die sind Wesentlich für ihre Natur - die Freundin ist eine Person, das Auto ist ein Landfahrzeug, die Katze ist ein Säugetier ...

Verhaltensweisen sollten zuerst Schnittstellen zugeordnet werden (einschließlich der Standardschnittstelle der Klasse) und nur dann in eine Basisklasse befördert werden, wenn sie (a) einer großen Gruppe von Klassen, die Teilmengen einer größeren Klasse sind, gemeinsam sind - im gleichen Sinne wie "Katze" und "Person" sind Teilmengen von "Säugetier".

Der Haken ist, nachdem Sie das objektorientierte Design ausreichend besser verstanden haben, als ich es zunächst getan habe, werden Sie dies normalerweise automatisch tun, ohne darüber nachzudenken. Die nackte Wahrheit der Aussage "Code an eine Schnittstelle, keine abstrakte Klasse" wird so offensichtlich, dass es Ihnen schwer fällt, zu glauben, dass jemand sich die Mühe machen würde, sie zu sagen - und versuchen, andere Bedeutungen darin zu lesen.

Eine andere Sache, die ich hinzufügen würde, ist, dass, wenn eine Klasse ist rein Abstract - ohne nicht abstrakte, nicht unterhörte Mitglieder oder Methoden, die Kind, Eltern oder Kunden ausgesetzt sind - warum ist es dann eine Klasse? Es könnte in einigen Fällen durch eine Schnittstelle und in anderen Fällen durch Null ersetzt werden.

Bevorzugen Sie Schnittstellen gegenüber abstrakten Klassen

Begründung, die wichtigsten Punkte, die [zwei bereits hier erwähnte] berücksichtigen müssen, sind:

  • Schnittstellen sind flexibler, da eine Klasse mehrere Schnittstellen implementieren kann. Da Java nicht mehrfach die Vererbung hat, verhindert die Verwendung abstrakter Klassen Ihre Benutzer daran, eine andere Klassenhierarchie zu verwenden. Bevorzugen Sie im Allgemeinen Schnittstellen, wenn keine Standardimplementierungen oder Status vorhanden sind. Java -Sammlungen bieten gute Beispiele dafür (Karte, Set usw.).
  • Abstrakte Klassen haben den Vorteil, eine bessere Vorwärtskompatibilität zu ermöglichen. Sobald Clients eine Schnittstelle verwenden, können Sie sie nicht ändern. Wenn sie eine abstrakte Klasse verwenden, können Sie dennoch Verhalten hinzufügen, ohne vorhandenen Code zu brechen. Wenn Kompatibilität ein Problem darstellt, sollten Sie abstrakte Klassen verwenden.
  • Auch wenn Sie Standardimplementierungen oder interne Status haben,Erwägen Sie, eine Schnittstelle und eine abstrakte Implementierung davon anzubieten. Dies hilft Kunden, erlaubt ihnen jedoch immer noch eine größere Freiheit, wenn sie gewünscht werden [1].
    Natürlich wurde das Thema an anderer Stelle ausführlich erörtert [2,3].

1] Es fügt natürlich mehr Code hinzu, aber wenn Kürze Ihr Hauptanliegen ist, hätten Sie Java wahrscheinlich in erster Linie vermieden sollen!

2] Joshua Bloch, effektiver Java, Artikel 16-18.

[3] http://www.codeprroject.com/kb/ar...

Frühere Kommentare zur Verwendung abstrakter Klassen für die allgemeine Implementierung sind definitiv in der Marke. Ein Vorteil, den ich noch nicht gesehen habe, ist, dass die Verwendung von Schnittstellen die Implementierung von Scheinobjekten zum Zwecke des Unit -Tests erheblich erleichtert. Wenn Sie IPET und Petbase als Jason Cohen beschrieben definieren, können Sie verschiedene Datenbedingungen leicht verspotten, ohne den Overhead einer physischen Datenbank (bis Sie entscheiden, dass es Zeit ist, die reale Sache zu testen).

Verwenden Sie keine Basisklasse, es sei denn, Sie wissen, was sie bedeutet, und in diesem Fall gilt sie. Wenn es gilt, verwenden Sie es ansonsten Schnittstellen. Beachten Sie jedoch die Antwort über kleine Schnittstellen.

Die öffentliche Erbschaft wird in OOD überbeansprucht und drückt viel mehr aus, als die meisten Entwickler erkennen oder bereit sind, es gerecht zu werden. Siehe das Liskov Substitutablity Prinzip

Kurz gesagt, wenn a "a" B ist, dann ist A nicht mehr als B und liefert nicht weniger als B für jede Methode, die es enthüllt.

Konzeptionell wird eine Schnittstelle verwendet, um eine Reihe von Methoden, die ein Objekt bereitstellt, formell und semi-förmlich zu definieren. Formal eine Reihe von Methodennamen und Signaturen bedeutet, semi-formally-lesbare Dokumentation, die mit diesen Methoden verbunden sind. Schnittstellen sind nur Beschreibungen einer API (schließlich steht API für den Anwendungsprogrammierer Schnittstelle), sie können keine Implementierung enthalten und es ist nicht möglich, eine Schnittstelle zu verwenden oder auszuführen. Sie machen nur den Vertrag, wie Sie mit einem Objekt interagieren sollten.

Klassen bieten eine Implementierung und können erklären, dass sie Null, eine oder mehrere Schnittstellen implementieren. Wenn eine Klasse geerbt werden soll, soll der Konvent den Klassennamen mit "Basis" vorfixieren.

Es gibt eine Unterscheidung zwischen einer Basisklasse und einem abstrakten Basisklassen (ABC). ABCS -Schnittstelle und -implementierung gemeinsam. Abstract außerhalb der Computerprogrammierung bedeutet "Zusammenfassung", dh "Abstract == Interface". Eine abstrakte Basisklasse kann dann sowohl eine Schnittstelle als auch eine leere, partielle oder vollständige Implementierung beschreiben, die als vererbt werden soll.

Meinungen darüber, wann Schnittstellen gegen abstrakte Basisklassen gegen nur Klassen verwendet werden sollen Dynamisch getippte Sprachen können auch Schnittstellen und abstrakte Basisklassen haben. In Python zum Beispiel wird die Unterscheidung zwischen einer Klasse klargestellt, was erklärt Geräte eine Schnittstelle und ein Objekt, das eine Instanz einer Klasse ist und zu sagen wird zur Verfügung stellen diese Schnittstelle. In einer dynamischen Sprache ist es möglich, dass zwei Objekte, die beide Instanzen derselben Klasse sind, erklären können, dass sie vollständig liefern anders Schnittstellen. In Python ist dies nur für Objektattribute möglich, während Methoden zwischen allen Objekten einer Klasse gemeinsam genutzt werden. In Ruby können Objekte jedoch pro Instanzmethoden aufweisen. Daher ist es möglich, dass die Schnittstelle zwischen zwei Objekten derselben Klasse so stark variieren kann wie der Programmierer (Ruby hat jedoch keine explizite Möglichkeit, Schnittstellen zu deklarieren).

In dynamischen Sprachen wird die Schnittstelle zu einem Objekt oft implizit angenommen, entweder durch Einbindung eines Objekts und die Frage, welche Methoden es bietet (schauen Sie vor dem Sprung) oder vorzugsweise, indem Sie einfach versuchen, die gewünschte Schnittstelle für ein Objekt zu verwenden und Ausnahmen zu fangen, wenn das Objekt fängt Liefert diese Schnittstelle nicht (leichter zu bitten Vergebung als die Erlaubnis). Dies kann zu "Fehlalarmen" führen, bei denen zwei Schnittstellen den gleichen Methodennamen haben, sich jedoch semantisch unterschiedlich unterscheiden von Ihrem Code.

Eine weitere Option, die Sie beachten sollten, ist die Verwendung der "Has-a" -Beziehung, auch bekannt als "" oder "Komposition". Manchmal ist dies ein sauberer, flexiblerer Weg, um Dinge zu strukturieren, als "is-a" -Beritanz zu verwenden.

Es ist vielleicht logisch nicht so viel Sinn zu sagen, dass Hund und Katze beide "ein Haustier haben", aber es vermeidet häufige Multiple -Vererbungs -Fallstricke:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Ja, dieses Beispiel zeigt, dass es viel Code -Duplikation und mangelnde Eleganz gibt, um Dinge so zu tun. Man sollte aber auch zu schätzen wissen, dass dies dazu beiträgt, Hund und Katze von der Haustierklasse zu entkoppeln (in diesem Hund und Katze haben keinen Zugang zu den privaten Mitgliedern von Haustier), und es lässt Hunde und Katze Raum, um von etwas anderem zu erben. -Possiblic die Säugetierklasse.

Die Komposition ist vorzuziehen, wenn kein privater Zugriff erforderlich ist und Sie nicht auf Hunde und Katze mit generischen PET -Referenzen/-zeigern verweisen müssen. Schnittstellen geben Ihnen diese generische Referenzfähigkeit und können dazu beitragen, die Ausführlichkeit Ihres Codes zu verringern, aber sie können auch die Dinge verschleiern, wenn sie schlecht organisiert sind. Die Vererbung ist nützlich, wenn Sie einen privaten Zugang zu Mitgliedern benötigen. Wenn Sie ihn verwenden, verpflichten Sie sich, Ihren Hund und Ihre Katzenkurse an Ihre Haustierklasse zu koppeln, was eine hohe Kosten für die Bezahlung darstellt.

Zwischen Vererbung, Zusammensetzung und Schnittstellen gibt es keinen Weg, der immer richtig ist, und es hilft zu überlegen, wie alle drei Optionen in Harmonie verwendet werden können. Von den drei ist die Vererbung in der Regel die Option, die am wenigsten häufig verwendet werden sollte.

Es hängt von Ihren Anforderungen ab. Wenn Ipet einfach genug ist, würde ich das vorziehen, dies zu implementieren. Andernfalls, wenn Petbase eine Menge Funktionalität implementiert, die Sie nicht duplizieren möchten, dann haben Sie es.

Der Nachteil der Implementierung einer Basisklasse ist die Voraussetzung für override (oder new) Bestehende Methoden. Dies macht sie virtuelle Methoden, was bedeutet, dass Sie vorsichtig sein müssen, wie Sie die Objektinstanz verwenden.

Zuletzt tötet mich das einzelne Erbe von .NET. Ein naives Beispiel: Sagen Sie, Sie stellen eine Benutzerkontrolle, sodass Sie erben UserControl. Aber jetzt sind Sie auch von Erben gesperrt PetBase. Dies zwingt Sie, neu zu organisieren, z. B. um eine zu machen PetBase Klassenmitglied stattdessen.

@Joel: Einige Sprachen (z. B. C ++) erlauben mehrere Ineritanz.

Normalerweise implementiere ich auch nicht, bis ich einen brauche. Ich bevorzuge Schnittstellen gegenüber abstrakten Klassen, da dies etwas mehr Flexibilität gibt. Wenn es in einigen der Erbeklassen ein gemeinsames Verhalten gibt, bewege ich das auf und mache eine abstrakte Basisklasse. Ich sehe nicht die Notwendigkeit beidem, da sie im Wesentlichen den gleichen Zweck erzwingen, und beides ist ein schlechter Code-Geruch (IMHO), dass die Lösung überbetont wurde.

In Bezug auf C#können in einigen Sinnen Schnittstellen und abstrakte Klassen austauschbar sein. Die Unterschiede sind jedoch: i) Schnittstellen können keinen Code implementieren; ii) Aus diesem Grund können Schnittstellen den Stapel zur Unterklasse nicht weiter oben aufrufen. und iii) kann nur eine abstrakte Klasse in einer Klasse vererbt werden, während mehrere Schnittstellen in einer Klasse implementiert werden können.

Durch Def bietet die Schnittstelle eine Ebene zur Kommunikation mit einem anderen Code. Alle öffentlichen Eigenschaften und Methoden einer Klasse sind standardmäßig implementiert, um eine implizite Schnittstelle zu implementieren. Wir können auch eine Schnittstelle als eine Rolle definieren, wenn jemals eine Klasse diese Rolle spielen muss, sie implementieren muss, um ihr je nach implementierender Klasse unterschiedliche Implementierungsformen zu geben. Wenn Sie also über die Schnittstelle sprechen, sprechen Sie über Polymorphismus und wenn Sie über die Basisklasse sprechen, sprechen Sie über Erbe. Zwei Konzepte von oops !!!

Ich habe festgestellt, dass ein Muster der Schnittstelle> Abstract> Beton im folgenden Anwendungsfall funktioniert:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

Die abstrakte Klasse definiert Standard -Shared -Attribute der konkreten Klassen, erzwingt jedoch die Schnittstelle. Zum Beispiel:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Da alle Säugetiere Haare und Brustwarzen haben (Afaik, ich bin kein Zoologiker), können wir dies in die abstrakte Basisklasse rollen

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

Und dann definieren die Betonklassen lediglich, dass sie aufrecht gehen.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Dieses Design ist schön, wenn es viele konkrete Klassen gibt, und Sie möchten nicht nur die Kesselplatte pflegen, nur um auf eine Schnittstelle zu programmieren. Wenn der Schnittstelle neue Methoden hinzugefügt würden, würde dies alle resultierenden Klassen brechen, sodass Sie immer noch die Vorteile des Schnittstellenansatzes erhalten.

In diesem Fall könnte das Abstract genauso gut konkret sein; Die abstrakte Bezeichnung hilft jedoch zu betonen, dass dieses Muster verwendet wird.

Ein Erbe einer Basisklasse sollte eine "ist eine" Beziehung haben. Die Schnittstelle repräsentiert eine Beziehung "implementiert eine" Beziehung. Verwenden Sie also nur eine Basisklasse, wenn Ihre Erben die IS -Beziehung beibehalten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top