Was ist der Unterschied zwischen einer Schnittstelle und einer Klasse und warum sollte ich eine Schnittstelle verwenden, wenn ich die Methoden direkt in der Klasse implementieren kann?

117

Ich bin mir bewusst, dass dies eine sehr grundlegende Frage ist, aber ein Interviewer hat mich sehr trickreich gefragt und ich war hilflos :(

Ich kenne nur materielle oder theoretische Definitionen für eine Schnittstelle und habe sie auch in vielen Projekten implementiert, an denen ich gearbeitet habe. Aber ich verstehe wirklich nicht, warum und wie das nützlich ist.

Ich verstehe auch nichts in der Benutzeroberfläche. dh wir verwenden zum Beispiel

conn.Dispose();in endlich blockieren. Aber ich sehe nicht, dass die Klasse die IDisposableinterface ( SqlConnection) -Klasse implementiert oder erbt , meine ich. Ich frage mich, wie ich einfach den Methodennamen aufrufen kann. In der gleichen Sache verstehe ich auch nicht, wie die Dispose-Methode funktioniert, da wir den Funktionskörper mit unserer eigenen Implementierung für alle Schnittstellenmethoden implementieren müssen. Wie werden Schnittstellen als Verträge akzeptiert oder benannt? Diese Fragen gingen mir bis jetzt immer wieder durch den Kopf und ehrlich gesagt habe ich nie einen guten Thread gesehen, der meine Fragen auf eine Weise erklären würde, die ich verstehen kann.

MSDN sieht wie immer sehr beängstigend aus und es ist dort keine einzige Zeile klar ( Leute, bitte entschuldigen Sie, wer sich in der Entwicklung auf hohem Niveau befindet. Ich bin der festen Überzeugung, dass jeder Code oder Artikel den Verstand von jedem erreichen sollte, der ihn sieht, daher, wie viele andere sagen, MSDN ist nicht von Nutzen ).

Der Interviewer sagte:

Er hat 5 Methoden und implementiert diese gerne direkt in der Klasse. Wenn Sie sich jedoch für eine abstrakte Klasse oder Schnittstelle entscheiden müssen, welche wählen Sie und warum? Ich habe ihm all die Dinge beantwortet, die ich in verschiedenen Blogs gelesen habe und die Vor- und Nachteile sowohl der abstrakten Klasse als auch der Schnittstelle sagten, aber er ist nicht überzeugt, er versucht, "Warum Schnittstelle" im Allgemeinen zu verstehen. "Warum abstrakte Klasse" im Allgemeinen, auch wenn ich die gleichen Methoden nur einmal implementieren kann und sie nicht ändern werde.

Ich sehe nirgendwo im Netz, ich könnte einen Artikel bekommen, der mich klar über Schnittstellen und deren Funktionsweise erklärt. Ich bin einer der vielen Programmierer, die noch nichts über Schnittstellen wissen (ich kenne die theoretischen Methoden und Methoden, die ich verwendet habe), aber nicht zufrieden sind, dass ich sie klar verstanden habe.

Lerner
quelle
4
Schnittstellen sind eine, die ich auch nur schwer verstehen kann. Gute Frage.
Brian
4
Programmieren auf einen abstrakten Vertrag anstatt auf eine konkrete Implementierung .... Kurz gesagt bedeutet dies, dass Sie jedes Objekt ersetzen können, das eine Schnittstelle implementiert, wenn eine Schnittstelle erforderlich ist.
Mitch Wheat
7
SqlConnectionerbt System.ComponentModel.Componentwelche implementiert IDisposable.
Lee
2
@MitchWheat - Es soll kein Beispiel sein, die Frage fragt, wie SqlConnectionimplementiert IDisposable.
Lee
Oh Lee, das hat mich verständlich gemacht, danke. Aber ich sehe immer noch nicht, wie oder wo die Funktionalität der "Dispose" -Methode definiert ist.
Lerner

Antworten:

92

Schnittstellen eignen sich hervorragend, wenn Sie so etwas erstellen möchten:

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

In meinem Beispiel könnte ich ein Entwickler sein, der schreibt MyLogClass, und die anderen Entwickler könnten ihre Klassen erstellen, und wenn sie sich protokollieren möchten, implementieren sie die Schnittstelle IMyLogInterface. Es ist so, als würden sie mich fragen, was sie implementieren müssen, um die WriteLog()Methode in zu verwenden MyLogClass. Die Antwort finden sie in der Oberfläche.

Guilherme de Jesus Santos
quelle
3
Hey, das scheint eine sehr gute Zutat für mich zu sein, ich weiß es wirklich zu schätzen, vielen Dank :) :)
Lerner
14
Meine Frage ist, ob Sie instanziieren MyClassund MyOtherClasswarum Sie nicht einfach anrufen, aClass.WriteLog()warum Sie diesen zusätzlichen Schritt hinzufügen. Die Implementierung von WriteLog()würde für jede der Klassen unterschiedlich bleiben, aber Sie haben das Objekt bereits. Warum sollten Sie es also an eine Handlerklasse übergeben?
Zach M.
Hm könnte es sein, dass es für andere einfacher wäre, Ihren Logger zu verwenden, ohne die Details zu kennen, wenn Sie Ihr Protokollierungsbeispiel auf Nugget setzen. Andererseits ist es immer noch keine universelle Klasse (ea, ich könnte eine schreiben Schnittstelle mit Protokollierung UND Zuweisungsebenen) Schnittstellen befinden sich immer noch nur in Ihrem Bereich. Also außer dir, denn wer profitiert davon?
user3800527
2
@ ZachM. Wenn ich recht habe, bedeutet die Antwort, dass er die Klassen nicht instanziiert, aber andere Entwickler werden die Klassen instanziieren und sie als Parameter an die MyLogClass WriteLogMethode übergeben. So kann seine Methode mit jedem Objekt umgehen, das implementiert wird IMyLogInterface. Hier ist ein weiterer interessanter Beitrag.
Shaijut
1
Meine Frage ist warum Interface ??? Das obige Szenario kann auch von einer abstrakten Klasse mit allen abstrakten Methoden erreicht werden.
Abhijit Ojha
52

Ein Grund, warum ich Schnittstellen verwende, ist, dass dies die Flexibilität des Codes erhöht. Angenommen, wir haben eine Methode, die ein Objekt vom Klassentyp Account als Parameter verwendet, z.

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

Das Problem dabei ist, dass der Methodenparameter auf eine Implementierung eines Kontos festgelegt ist. Dies ist in Ordnung, wenn Sie niemals einen anderen Kontotyp benötigen würden. Nehmen Sie dieses Beispiel, das stattdessen eine Kontoschnittstelle als Parameter verwendet.

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

Diese Lösung ist nicht auf eine Implementierung festgelegt, was bedeutet, dass ich ihr ein SuperSavingsAccount oder ein ExclusiveAccount (beide implementieren die IAccount-Schnittstelle) übergeben und für jedes implementierte Konto ein anderes Verhalten erhalten kann.

user2211290
quelle
45

Schnittstellen sind Verträge, denen Implementierer folgen müssen. Abstrakte Klassen ermöglichen Verträge und gemeinsame Implementierungen - etwas, das Interfaces nicht haben können. Klassen können mehrere Schnittstellen implementieren und erben. Klassen können nur eine einzelne abstrakte Klasse erweitern.

Warum Schnittstelle

  • Sie haben keine Standard- oder Shared-Code-Implementierung
  • Sie möchten Datenverträge (Webdienste, SOA) freigeben.
  • Sie haben unterschiedliche Implementierungen für jeden Schnittstellenimplementierer ( IDbCommandhat SqlCommandund OracleCommandimplementiert die Schnittstelle auf bestimmte Weise )
  • Sie möchten Mehrfachvererbung unterstützen .

Warum abstrakt?

SliverNinja - MSFT
quelle
2
@Silver Ich habe das meiste gelesen, was Sie in Blogs geschrieben haben, aber ich versuche, es praktisch zu verstehen. Ich habe WCF-Dienste ausgeführt, Schnittstellen verfügbar gemacht (aber es war nur eine einzelne eigenständige App ohne Upstream oder Downstream). Daher konnte ich es nicht richtig verstehen, obwohl ich Schnittstellen sehr gut entworfen und implementiert habe. Meine Frage ist praktisch, Sie teilen nur die Methodennamen, die Vertrag bedeutet, oder? WIE IST DAS NÜTZLICH? (( Ich weiß, es zwingt nur dazu, alle Methoden zu implementieren, aber wie? In Ihrem Beitrag oben auf der Benutzeroberfläche, 2. Punkt, heißt Teilen, können Sie ein praktisches Echtzeitbeispiel dafür geben
Learner
1
Ein praktisches Beispiel in Bezug auf Schnittstellen und SOA, wir teilen unsere WCF - Schnittstellen ( DataContracts) in einer .NET - Assembly ( zB Contracts.Shared.dll ) , so dass .NET - Client Verbraucher können einfach miteinander arbeiten mitChannelFactory ( Erzeugungscode über Dienstverweis hinzufügen zu vermeiden usw. ) oder mit Service - Referenz mit Gemeinschaftstypen hinzufügen
SliverNinja - MSFT
Wenn ich nur abstrakte Methoden in einem abstrakten Calss deklariere, dann fungiert die abstrakte Klasse als Schnittstelle. Warum brauchen wir dann eine Schnittstelle?
Abhijit Ojha
25

Geben Sie hier die Bildbeschreibung ein

In diesem Beispiel weiß der PowerSocket also nichts anderes über die anderen Objekte. Die Objekte hängen alle von der vom PowerSocket bereitgestellten Leistung ab, sodass sie IPowerPlug implementieren und auf diese Weise eine Verbindung herstellen können.

Schnittstellen sind nützlich, da sie Verträge bereitstellen, mit denen Objekte zusammenarbeiten können, ohne dass Sie etwas anderes voneinander wissen müssen.

Brijesh Rana
quelle
Dies ist sinnvoll, aber ich habe immer noch Schwierigkeiten zu verstehen, ob Sie einfach keine Basisklasse für PowerSocket erstellen können und all diese anderen Dinge sie bei Bedarf erben. Technisch gesehen haben Steckdosen keine Ahnung von den anderen Klassen.
altaaf.hussein
Ich denke, weil Mehrfachvererbung in C #
Hugh Seagraves
22

Mit einem Wort - wegen Polymorphismus !

Wenn Sie "Auf eine Schnittstelle programmieren, nicht auf eine Implementierung", können Sie verschiedene Objekte, die dieselbe Schnittstelle (denselben Typ) verwenden, als Argument in die Methode einfügen. Auf diese Weise ist Ihr Methodencode nicht mit einer Implementierung einer anderen Klasse gekoppelt, was bedeutet, dass er immer offen ist, um mit neu erstellten Objekten derselben Schnittstelle zu arbeiten. (Öffnungs- / Schließprinzip)

  • Schauen Sie sich Dependency Injection an und lesen Sie unbedingt Design Patterns - Elemente wiederverwendbarer objektorientierter Software von GOF.
Gabriel C. Troia
quelle
4

C # hat keine Ententypisierung - nur weil Sie wissen, dass eine bestimmte Methode in einer Reihe konkreter Klassen implementiert ist, heißt das nicht, dass Sie sie beim Aufrufen dieser Methode alle gleich behandeln können. Durch die Implementierung einer Schnittstelle können Sie alle Klassen, die sie implementieren, hinsichtlich der Definition dieser Schnittstelle als dieselbe Art von Dingen behandeln.

Erix
quelle
3
In .net4 können Sie mit dem dynamischen Typ eine Art Ententyping erhalten.
Tony Hopkinson
4

Ich glaube, dass beim Stellen dieser Fragen bereits viel Blut vergossen wurde, und viele versuchen, dieses Problem zu lösen, indem sie roboterähnliche Begriffe erklären, die kein normaler Mensch verstehen kann.

So zuerst. Um zu erfahren, warum Schnittstelle und warum abstrakt, müssen Sie lernen, wofür sie sind. Ich persönlich habe diese beiden bei der Anwendung der Factory Class gelernt. Unter diesem Link finden Sie eine gute Anleitung

Lassen Sie uns nun auf den Link eingehen, den ich bereits gegeben habe.

Sie haben eine Fahrzeugklasse , die sich je nach Benutzeranforderung ändern kann (z. B. Hinzufügen von LKW , Tank , Flugzeug usw.)

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

und

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

und beide haben Vertrag IChoice, die einfach sagen, dass meine Klasse Kaufmethode haben sollte

public interface IChoice
{
    string Buy();
}

Sie sehen, diese Schnittstelle erzwingt nur die Methode Buy(), lässt jedoch die geerbte Klasse entscheiden, was zu tun ist, wenn sie sie implementiert. Dies ist die Einschränkung von Interface. Wenn Sie nur Interface verwenden, wiederholen Sie möglicherweise eine Aufgabe, die wir mithilfe von abstact automatisch implementieren können. In unserem Beispiel hat der Kauf jedes Fahrzeugs einen Rabatt.

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

Wenn Sie jetzt die Factory-Klasse verwenden, können Sie dasselbe erreichen, aber wenn Sie abstract verwenden, lassen Sie die Basisklasse die Buy()Methode ausführen .

Zusammenfassend: Schnittstellenverträge lassen die ererbte Klasse die Implementierung durchführen, während abstrakte Klassenverträge die Implementierung initialisieren können (die von der ererbten Klasse überschrieben werden kann).

dr.Crow
quelle
2

Mit einer Schnittstelle können Sie Folgendes tun:

1) Erstellen Sie getrennte Schnittstellen, die unterschiedliche Schnitte Ihrer Implementierung bieten und eine zusammenhängendere Schnittstelle ermöglichen.

2) Lassen Sie mehrere Methoden mit demselben Namen zwischen den Schnittstellen zu, da Sie keine widersprüchliche Implementierung haben, sondern nur eine Signatur.

3) Sie können Ihre Benutzeroberfläche unabhängig von Ihrer Implementierung versionieren und ausgliedern, um sicherzustellen, dass ein Vertrag eingehalten wird.

4) Ihr Code kann sich eher auf Abstraktion als auf Konkretion stützen, was eine intelligente Abhängigkeitsinjektion ermöglicht, einschließlich der Injektion von Test-Mocks usw.

Ich bin mir sicher, dass es noch viele weitere Gründe gibt, dies sind nur einige.

Mit einer abstrakten Klasse können Sie eine teilweise konkrete Basis haben, von der aus Sie arbeiten können. Dies ist nicht dasselbe wie eine Schnittstelle, hat jedoch ihre eigenen Eigenschaften, z. B. die Möglichkeit, eine teilweise Implementierung mithilfe des Template-Methodenmusters zu erstellen.

Jeff Watkins
quelle
Sie haben das Wichtigste vernachlässigt: Durch die Implementierung einer Schnittstelle kann Ihre Klasse von jedem Code verwendet werden, der eine Implementierung dieser Schnittstelle benötigt, ohne dass der betreffende Code etwas über Ihre Klasse wissen muss.
Supercat
2

Als echtes Beispiel für @ user2211290 Antwort:

Beide Arrayund Listhaben Schnittstelle IList. Unten haben wir ein string[]und ein List<string>und überprüfen beide mit einer Methode unter Verwendung von IList :

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

//a methode that manipulates both of our collections with IList
static void CheckForDigit(IList collection, string digit)
{
    Console.Write(collection.Contains(digit));
    Console.Write("----");
    Console.WriteLine(collection.ToString()); //writes the type of collection
}

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}
Farzin Kanzi
quelle
1

Sie können nur von einer abstrakten Klasse erben. Sie können von mehreren Schnittstellen erben. Dies bestimmt, was ich in den meisten Fällen verwende.

Der Vorteil einer abstrakten Klasse wäre, dass Sie eine Basisimplementierung haben können. Im Fall von IDisposable ist eine Standardimplementierung jedoch nutzlos, da die Basisklasse nicht weiß, wie die Dinge ordnungsgemäß bereinigt werden sollen. Somit wäre eine Schnittstelle besser geeignet.

Jeow Li Huan
quelle
1

Sowohl die abstrakte Klasse als auch die Schnittstelle sind Verträge.

Die Idee eines Vertrags ist, dass Sie ein bestimmtes Verhalten angeben. Wenn Sie sagen, dass Sie implementiert haben, haben Sie dem Vertrag zugestimmt.

Die Wahl von abstrakt über Schnittstelle ist.

Jeder nicht abstrakte Nachkomme der abstrakten Klasse wird den Vertrag implementieren.

gegen

Jede Klasse, die die Schnittstelle implementiert, implementiert den Vertrag.

Sie verwenden also abstrakt, wenn Sie ein Verhalten angeben möchten, das alle Nachkommen implementieren müssen, und sparen sich das Definieren einer separaten Schnittstelle. Jetzt muss alles, was diesen effektiv aggregierten Vertrag erfüllt, ein Nachkomme sein.

Tony Hopkinson
quelle
1

Schnittstellen sollen eine Abstraktion (einen Archetyp) der Abstraktion (der Klassen) der Realität (der Objekte) machen.

Schnittstellen sollen Vertragsbedingungen angeben, ohne die Implementierung durch Klassen bereitzustellen.

Schnittstellen sind Spezifikationen:

  • Schnittstellen sind Entwurfszeitartefakte, um das unbewegliche Verhalten des Konzepts zu spezifizieren, da es allein und statisch ist.

  • Klassen sind Artefakte der Implementierungszeit, um die mobile Struktur der Realität während der Interaktion und Bewegung zu spezifizieren.

Was ist eine Schnittstelle?

Wenn Sie eine Katze beobachten, können Sie sagen, dass es sich um ein Tier handelt, das vier Pfoten, einen Kopf, einen Stamm, einen Schwanz und Haare hat. Sie können sehen, dass er laufen, rennen, essen und miauen kann. Und so weiter.

Sie haben gerade eine Schnittstelle mit ihren Eigenschaften und Operationen definiert. Als solche haben Sie keinen Modus operandi definiert, sondern nur Funktionen und Fähigkeiten, ohne zu wissen, wie die Dinge funktionieren: Sie haben Fähigkeiten und Unterscheidungen definiert.

Als solches ist es noch keine Klasse, obwohl wir dies in UML eine Klasse in einem Klassendiagramm nennen, da wir private und geschützte Mitglieder definieren können, um einen tiefen Überblick über das Artefakt zu erhalten. Seien Sie hier nicht verwirrt, denn in UML ist eine Schnittstelle etwas anders als eine Schnittstelle in C #: Sie ist wie ein partieller Zugriffspunkt auf das Abstraktionsatom. Als solches haben wir gesagt, dass eine Klasse mehrere Schnittstellen implementieren kann. Als solches ist es dasselbe, aber nicht, da Schnittstellen in C # sowohl zum Abstrahieren der Abstraktion als auch zum Einschränken dieser Abstraktion als Zugriffspunkt verwendet werden. Es gibt zwei verschiedene Verwendungszwecke. Somit repräsentiert eine Klasse in UML eine vollständige Kopplungsschnittstelle zu einer Programmierklasse, während eine UML-Schnittstelle eine Entkopplungsschnittstelle eines Abschnitts einer Programmierklasse darstellt. Tatsächlich, Das Klassendiagramm in UML kümmert sich nicht um die Implementierung und alle seine Artefakte befinden sich auf der Ebene der Programmierschnittstelle. Während wir UML-Klassen Programmierklassen zuordnen, handelt es sich um eine Umsetzung der abstrakten Abstraktion in eine konkrete Abstraktion. Es gibt eine Subtilität, die die Dichotomie zwischen dem Bereich Design und dem Bereich Programmierung erklärt. Eine Klasse in UML ist also eine Programmierklasse aus der Sicht einer Programmierschnittstelle, während innere verborgene Dinge berücksichtigt werden.

Schnittstellen ermöglichen es auch, Mehrfachvererbung zu simulieren, wenn sie nicht auf unangenehme Weise verfügbar sind. Beispielsweise implementiert die cat-Klasse die cat-Schnittstelle, die sich von der animal-Schnittstelle ableitet. Diese Katzenklasse implementiert auch diese Schnittstellen: Gehen, Laufen, Essen und Geräusche machen. Dies kompensiert das Fehlen einer Mehrfachvererbung auf Klassenebene, aber jedes Mal, wenn Sie alles neu implementieren müssen, können Sie die Realität bestenfalls nicht so faktorisieren, wie es die Realität selbst tut.

Um zu verstehen, dass wir uns auf die Pascal-Objektcodierung beziehen können, definieren Sie in einer Einheit die Schnittstelle und die Implementierungsabschnitte. In der Schnittstelle definieren Sie die Typen und in der Implementierung implementieren Sie den Typ:

unit UnitName;

interface

type
  TheClass = class
  public
    procedure TheMethod;
  end;

implementation

class procedure TheClass.TheMethod;
begin
end;

Hier stimmt der Schnittstellenabschnitt mit dem UML-Klassendesign überein, während Schnittstellentypen also andere Dinge sind.

In unserem Geschäft haben wir also ein Wort, die Schnittstelle , um zwei unterschiedliche, aber ähnliche Dinge zu benennen, und dies ist eine Quelle der Verwirrung.

Auch in C # können Programmierschnittstellen beispielsweise das Fehlen eines echten generischen Polymorphismus bei offenen Typen kompensieren, ohne das Ziel wirklich zu erreichen, da Sie die stark typisierte Habilität verloren haben.

Schließlich sind Schnittstellen erforderlich, damit inkompatible Systeme kommunizieren können, ohne sich um die Implementierung und Verwaltung von Objekten im Speicher kümmern zu müssen, wie sie mit dem (verteilten) Common Object Model eingeführt wurden.

Was ist eine Klasse?

Nachdem Sie eine Reduktion der Realität von außen definiert haben, können Sie sie aus einer inneren Perspektive beschreiben: In dieser Klasse definieren Sie Datenverarbeitung und Nachrichtenverwaltung, damit die von Ihnen gekapselte Realität zum Leben erweckt wird und dank interagiert zu Objekten mit Instanzen.

In UML realisieren Sie also ein fraktales Eintauchen in die Räder der Maschinerie und beschreiben die Zustände, die Wechselwirkungen usw., um die Abstraktion des Fragments der Realität, mit der Sie umgehen möchten, implementieren zu können.

Als solche ist eine abstrakte Klasse aus Sicht des Compilers irgendwie das Äquivalent einer Schnittstelle.

Mehr Informationen

Protokoll (objektorientierte Programmierung)

C # - Schnittstellen

C # - Klassen

Olivier Rogier
quelle
1

Lassen Sie mich Ihnen von fliegenden Toastern erzählen.

fliegender Toaster

Es gibt natürlich viele Situationen, in denen Sie ein funktionierendes Softwaresystem erstellen können, ohne Schnittstellen zu deklarieren oder zu implementieren: Jedes objektorientierte Software-Design kann nur mit Klassen realisiert werden.

Andererseits kann jedes Softwaresystem auch in Assemblersprache oder besser noch in Maschinencode implementiert werden. Der Grund, warum wir Abstraktionsmechanismen verwenden, liegt darin, dass sie dazu neigen, die Dinge einfacher zu machen. Schnittstellen sind ein solcher Abstraktionsmechanismus.

Es kommt also einfach so vor, dass es bestimmte nicht triviale objektorientierte Designs gibt, die so viel einfacher zu implementieren sind, wenn Sie Schnittstellen verwenden, dass Schnittstellen in diesen Fällen praktisch notwendig werden.

Diese nicht trivialen Entwürfe haben mit Mehrfachvererbung zu tun, was in ihrer "wahren" Form der Fall ist, wenn eine Klasse nicht nur von einer Basisklasse, sondern von zwei oder mehr Basisklassen erbt. Diese wahre Form ist in C # nicht möglich, aber bevor Sprachen wie C # und Java ins Leben gerufen wurden, war die maßgebliche Sprache C ++, das die echte Mehrfachvererbung vollständig unterstützt. Leider stellte sich heraus, dass eine echte Mehrfachvererbung keine sehr gute Idee ist, da sie das Design der Sprache immens kompliziert und auch verschiedene Probleme aufwirft, zum Beispiel das berühmte "Diamantproblem". (Siehe "Was ist das genaue Problem bei der Mehrfachvererbung?" Antwort von J Francis )

Wenn also jemand eine "fliegende Toaster" -Klasse bauen wollte, würde er von einer vorhandenen "Toaster" -Klasse und auch von einer vorhandenen "fliegenden" Klasse erben. Das Problem, auf das sie wahrscheinlich stießen, war, dass die Stromversorgung der Toasterklasse wahrscheinlich eine Steckdose war, während die Stromversorgung der Flugmaschinenklasse wahrscheinlich Taubenfutter war, und die daraus resultierende neue Klasse auch irgendwie beides haben, oder es wäre unklar, welches es haben würde. (Das Diamantproblem.)

Die Entwickler von Sprachen wie C # und Java haben beschlossen, keine echte Mehrfachvererbung zuzulassen, um die Sprache einfach zu halten und Fallstricke wie das Diamond-Problem zu vermeiden. Es ist jedoch immer noch eine Form der Mehrfachvererbung erforderlich (oder zumindest sehr wünschenswert), sodass in diesen Sprachen Schnittstellen eingeführt wurden, um eine geringere Form der Mehrfachvererbung zu unterstützen und gleichzeitig die Probleme und die Komplexität einer echten Mehrfachvererbung zu vermeiden.

In dieser kleineren Form der Mehrfachvererbung dürfen Sie keine Klasse haben, die von mehr als einer Basisklasse erbt, aber Sie können zumindest von einer oder mehreren Schnittstellen erben. Wenn Sie also einen fliegenden Toaster bauen möchten, können Sie nicht sowohl von einer vorhandenen Toasterklasse als auch von einer vorhandenen fliegenden Klasse erben. Sie können jedoch von einer vorhandenen Toasterklasse erben und dann auch eine fliegende Schnittstelle verfügbar machen, die Sie selbst implementieren. möglicherweise mit den Mitteln, die Sie bereits von Toaster geerbt haben.

Wenn Sie also nie das Bedürfnis haben, eine Klasse zu erstellen, die zwei verschiedene und nicht verwandte Funktionssätze zusammenfasst, benötigen Sie keine Form der Mehrfachvererbung, sodass Sie keine Schnittstellen deklarieren oder implementieren müssen.

Mike Nakis
quelle
0

Mithilfe von Schnittstellen kann der Klassenentwickler die verfügbaren Methoden für den Endbenutzer sehr deutlich machen. Sie sind auch ein wesentlicher Bestandteil des Polymorphismus.

poy
quelle
Gut gesagt auf Ihrer 1. Aussage. Aber ich verstehe Ihre 2. Aussage nicht. Könnten Sie bitte ein Echtzeitbeispiel ausarbeiten?
Lerner
0

Ich werde die Definition einer Schnittstelle für eine abstrakte Klasse nicht veröffentlichen, da ich denke, dass Sie die Theorie sehr gut kennen und ich gehe davon aus, dass Sie die SOLID-Prinzipien kennen, also lassen Sie uns praktisch werden.

Wie Sie wissen, können Schnittstellen keinen Code enthalten, sodass die Nachteile recht einfach zu verstehen sind.

Wenn Sie die Eigenschaft Ihrer Klasse initialisieren müssen, die einen Konstruktor bereitstellt, oder einen Teil der Implementierung bereitstellen möchten, passt eine abstrakte Klasse gut zu einer Schnittstelle, die dies nicht zulässt.

Im Allgemeinen sollten Sie abstrakte Klassen Schnittstellen vorziehen, wenn Sie dem Client einen Konstruktor oder Code bereitstellen müssen, der Ihre Klasse erbt / erweitert

Massimiliano Peluso
quelle
-2

Abstrakte Klassen werden für verwandte Entitäten erstellt, wobei Schnittstellen für nicht verwandte Entitäten verwendet werden können.

Zum Beispiel, wenn ich zwei Entitäten habe, die Tier und Mensch sagen, dann werde ich mich für Interface entscheiden, wo, als ob ich im Detail Tiger, Löwe sagen möchte und mit Tier in Beziehung treten möchte, dann Tier abstrakte Klasse wählen werde.

wird wie unten aussehen

   Interface             
   ____|____
  |        |
Animal   Human



  Animal (Abstract class)
   __|___
  |      |
Tiger   Lion
Manoj
quelle