Laden...

Interface, Klassen und Vererbung ?

Letzter Beitrag vor 18 Jahren 42 Posts 33.329 Views
Interface, Klassen und Vererbung ?

Hallo zusammen. Mittlerweile bin ich schon ziemlich weit in meinem Anfänger C# Buch. Nun habe ich gerade die Interfaces und abstrakten Klassen durchgenommen. Hat auch alles gut funktioniert. Nach einer Lektion stelle ich mir immer eine Aufgabe, um sicher zu stellen, das ich den Stoff auch wirklich verstanden haben. Nun aber stehe ich vor einem Problem. Ich finde einfach keine Anwendungsmöglichkeit.

Ich habe mal eine Basis Klasse Tier geschrieben und von dieser eine Klasse Pferd abgeleitet. Dort konnte ich dann die virtuellen Methoden gebrauchen. Aber Interfaces? Könnte mir hier jemand ein konkretes Beispiel machen, wann die und die abstrakten Klassen zum Einsatz kommen?

Ich weiss, hört sich sicher blöd an, aber mir fällt einfach nichts ein.
Vielen Dank schon mal im Voraus

Hallo AlinghiFan,

na, für abstrakte Klassen hast du doch schon ein gutes Beispiel gegeben: Tier. Es ist nicht möglich bzw. sinnvoll ein allgemeines Tier-Objekt zu erzeugen, sondern nur Tiere konkreter Art, also z.B. Pferd. Ein weiteres Beispiel ist einer abstrakten Klasse ist GrafischesObjekt und mit Kreis, Rechteck u.ä. als konkrete Unterklassen. Abstrakte Klassen werden eingesetzt, um abstrakte Begriffe der realen Welt zu repräsentieren. Sie definieren die allgemeinen Eigenschaften und Fähigkeiten, die alle konkreten Unterklassen besitzen (sollen).

Ein Beispiel für ein Interface ist z.B. IEnumerable. Objekte einer Klasse, die IEnumerable implementiert, also z.B. Array, ArrayList, Hashtable, Stack u.ä., können dann unabhängig von der konkreten Klasse alle in der gleichen Weise über das Interface benutzt werden benutzt. Im Fall von IEnumerable z.B. in foreach-Schleifen.
Interfaces beschreiben meisten bestimmte Eigenschaften und Fähigkeiten, die man sich von den implementieren Klassen (zusätzlich) wünscht, wie aufzählbar, editierbar, formatierbar, serialisierbar usw.

HTH

herbivore

Hallo Community,

im Zusammengang mit "Erklärungsbedarf Schnittstelle" ist mir noch eingefallen, dass Schnittstellen ursprünglich als Ersatz für Mehrfachvererbung in die OOP eingeführt wurden, um die Probleme der Mehrfachvererbung zu umgehen.

Mehrfachvererbung bedeutet, dass eine Klasse von mehr als einer Oberklasse erbt. Probleme entstehen immer dann, wenn verschiedene dieser Oberklassen (direkt oder indirekt) gleichnamige "Bestandteile", z.B. Instance-Variablen enthalten. Die Frage ist dann, ob der entsprechende Bestandteil einmal oder mehrfach in der Unterklasse enthalten ist, und wenn mehrfach, wie und wo auf welchen Bestandteil zugegriffen wird.

Dieses Problem mit der Mehrfachververerbung hat man immer, wenn die Oberklassen selbst eine gemeinsame Oberklasse haben, z.B. bei


        Fahrzeug
         /     \
Landfahrzeug Wasserfahrzeug
         \     /
    Amphibienfahrzeug

Da Interfaces "nur" leere Hüllen sind, gibt es diese Probleme nicht. Wenn man mehrere Interfaces implementieren muss und dabei eine Namensgleichkeit auftritt, muss (und kann) man die entsprechende Methode oder Eigenschaft trotzdem nur einmal implementieren.

herbivore

Naja, interfaces wurden eigentlich nicht als Ersatz für MI eingeführt, sondern als Mittel der Abstraktion. Dass man damit MI weitgehend ersetzen kann, ist eher ein Nebeneffekt.

MfG VizOne

Hallo VizOne,

da man mit Mehrfachvererbung (im Zusammengang mit abstrakten Klassen) alles kann, was man mit Interfaces kann, braucht man Interfaces nur, wenn man keine Mehrfachvererbung hat. Kennst Du eine Sprache, die Mehrfachvererbung kann und Interfaces?

Ich ziehe vorsichtshalber mein "als Ersatz für" im zeitlichen Sinn zurück (wenn ich drüber nachdenke, ich weiß nicht sicher, ob Interfaces oder Mehrfachvererbung älter sind), behaupte aber nachwievor noch "als Ersatz für" Sinne von alternativen Konzepten mit gleichem oder zumindest ähnlichem Einsatzbereich und Ziel.

herbivore

Original von herbivore
Hallo VizOne,

da man mit Mehrfachvererbung (im Zusammengang mit abstrakten Klassen) alles kann, was man mit Interfaces kann, braucht man Interfaces nur, wenn man keine Mehrfachvererbung hat. Kennst Du eine Sprache, die Mehrfachvererbung kann und Interfaces?

Ja, z.B. C++ im gewissen Sinne. Interfaces gibt es da zwar nicht direkt als Sprachelement, aber sie sind definiert als abstrakte Klassen die nur öffentliche, rein virtuelle Methoden haben und keine Felder, also etwa:


// der Einfachheit halber struct, da Standardsichtbarkeit hier public
struct IMeinInterface {
  virtual void MeineMethode() = 0;
  virtual void NochEineMethode() = 0;
};

Nun bietet C++ die Möglichkeit für sowohl Interface-Vererbung (public erben, wobei das interface der geerbten Klasse öffentlich zugänglich wird) als auch Implementationsvererbung (protected/private erben, wodurch die Methoden der ererbten Klasse nur intern zur Verfügung stehen). Man könnte beispielsweise private von mehreren Implementationen erben und öffentlich von einem interface:


class VonAllemEtwas 
  : public IMeinInterface, 
    private EineKlasse,
    private EineAndereKlasse {
 //...
};

(natürlich kann man auch eine öffentliche (Mehrfach-)-Verberbung von konkreten Klassen haben, wodurch man Interface- und Implementationsvererbung vermischt).
Wenn man so erbt, hat man auf der einen Seite das Interface-System, auf der anderen Seite Mehrfachvererbung, was die Implementation angeht.

Ich finde also schon, dass man sagen kann, dass C++ beide Konzepte unterstützt. Natürlich ist das alles relativierbar, da bei C++ wie gesagt interfaces nur eine Art Absprache sind und kein syntaktisches Sprachmittel. Dass in Sprachen wie C# und Java interfaces so bestehen, wie sie bestehen, ist wohl darauf zurückzuführen, dass die Designer eine deutliche Trennung zwischen Implementationen und Interfaces haben wollten.

MfG VizOne

im grunde kann man interfaces in .NET fast mit abstracten klassen (die ausschliesslich abstracte member besitzen) gleichsetzen...
mit den einzigen unterschieden, dass der begriff "class" gegen den begriff "interface" vertauscht wird (um die mehrfachimplementierung zu ermöglichen) und dass interface-members natürlich immer "public" sind.

grtz
chief

Hallo VizOne,

das klingt so, als ob du mir widersprichst, dabei sagst du genau, was ich gesagt habe:

herbivore: da man mit Mehrfachvererbung (im Zusammengang mit abstrakten Klassen) alles kann, was man mit Interfaces kann, braucht man Interfaces [als Sprachmittel] nur, wenn man keine Mehrfachvererbung hat.

C++ hat eben Mehrfachvererbung und braucht gerade deshalb keine Interfaces (als Sprachmittel).

VizOne: Dass in Sprachen wie C# und Java interfaces so bestehen, wie sie bestehen, ist wohl darauf zurückzuführen, dass die Designer eine deutliche Trennung zwischen Implementationen und Interfaces haben wollten.

Wenn es nur um die Trennung an sich ginge, wäre m.E. nicht (ausreichend) zu erklären, warum es zwar mehrfache Interface-Vererbung gibt, aber nur einfache Implementierungsvererbung.

Ich denke daher nicht, dass diese Trennung das primäre Ziel war, sondern vielmehr, dass das Ziel war, den Teil der Mehrfachvererbung zu retten, der keine Schwierigkeiten macht (Interface-Vererbung) und auf den Teil der Mehrfachvererbung zu verzichten, der Probleme macht (Implementierungsvererbung) und man zur Erreichung dieses Ziels die Trennung eingeführt hat (zumindest bei Java und C#).

Gruß

herbivore

Naja, ich hab ja auch keine wissenschaftlichen Grundlagen darüber, warum interfaces nun ihren Weg in Java oder C# gefunden haben. Lassen wir uns also einfach darüber freuen, dass sie gibt und nach bestem Wissen und Gewissen nutzen 😉

MfG VizOne

Hallo Ihr!

Ihr redet euch alle so leicht kommt es mir vor! 🙂
Vieleicht kann mir von euch einer sagen wie ich in c# ein eigenes Interface Definieren kann, und dann zum Einsatz bringe? 🤔

Vielen Dank im Voraus!

Gruss, GIJOE

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

Hallo GIJOE,

hier ist schon ein Interface-Beispiel: MessageFilter - Wert an Formular übergeben

herbivore

ein schönes beispiel ist immer eine host/module-anwendung...



public class Host
{
    public readonly IModule[] Modules;
}


public interface IModule : IDisposable
{
    Host Manager { get; }
    string Name { get; }

    void Initialize(Host manager);
    void Run();
    void Dispose();
}


die klasse "Host" hat als eigenschaft ein array von "IModule"-objekten.
sie kann stets auf verschiedene methoden eines jeden objektes zugreifen, da festgelegt ist, dass eine klasse, die "IModule" implementiert, auf jeden fall diese members besitzen und definieren muss.
wie sie das tut, spielt im grunde keine rolle; und das interessiert das "Host"-objekt auch gar nicht, denn es ist alleinig von bedeutung, dass die vorgeschriebenen members vorhanden sind.

grtz
chief

Ich bin euch ehrlich dankbar für die Beispiele und erklärungen.
Aber in meinem Kopf ist nur Bahnhof!

Kann mir bitte einer ein kleines Beispiel anhand eines Projekts zur ferfügung stellen, aber bitte nur so eins, wo das allernötigste drin ist. Sonst blick ich wider nicht durch! 🙁

Kann mir dank eurer erklärung schon ungefähr vorstellen für was ein Interface gut ist, aber halt noch nicht so ganz! 🤔

Danke im Voraus!

Gruss, GIJOE

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

letzter versuch...



namespace Ultimate
{

    public class DataStoreController
    {
        public readonly IDataStoreProvider[] Providers;

        public DataStoreController(string pluginDirectory)
        {
            // durchsucht das angegebene Verzeichnis nach assemblies,
            // welche Klassen vom Typ 'IDataStoreProvider' enthalten
            // und lädt diese in ein array...
            this.Providers = new IDataStoreProvider[3];
            this.Providers[0] = Activator.CreateInstance(Type.GetType("XmlDataStoreProvider"));
            this.Providers[1] = Activator.CreateInstance(Type.GetType("StreamDataStoreProvider"));
            this.Providers[2] = Activator.CreateInstance(Type.GetType("SqlDataStoreProvider"));

            foreach (IDataStoreProvider provider in this.Providers)
                provider.Initialize(this);
        }

        public void StoreToAll(string data)
        {
            foreach (IDataStoreProvider provider in this.Providers)
                provider.StoreData(data);
        }
        public string ReceiveFrom(string type)
        {
            foreach (IDataStoreProvider provider in this.Providers)
                if (type == provider.Type)
                    return provider.RecoverData();
        }
        public void CloseDown()
        {
            foreach (IDataStoreProvider provider in this.Providers)
                provider.Dispose();
        }

    }


    public interface IDataStoreProvider : System.IDisposable
    {
        DataStoreController Controller { get; }
        string Type { get; }

        void Initialize(DataStoreController controller);
        void StoreData(string data);
        string RecoverData();
    }


    public class XmlDataStoreProvider : System.Xml.XmlDocument, IDataStoreProvider
    {
        private DataStoreController _Controller;

        public DataStoreController Controller
        {
            get { return this._Controller; }
        }
        public string Type
        {
            get { return "Xml"; }
        }

        public void Initialize(DataStoreController controller)
        {
            this._Controller = controller;
        }
        public void StoreData(string data)
        {
            // speichert die Daten in Xml-Form...
        }
        public string RecoverData()
        {
            // lädt die Daten aus Xml...
            return "xml stored data";
        }
        public void Dispose()
        {
            // schliesst die Ressource...
        }
    }


    public class StreamDataStoreProvider : System.IO.Stream, IDataStoreProvider
    {
        private DataStoreController _Controller;

        public DataStoreController Controller
        {
            get { return this._Controller; }
        }
        public string Type
        {
            get { return "Stream"; }
        }

        public void Initialize(DataStoreController controller)
        {
            this._Controller = controller;
        }
        public void StoreData(string data)
        {
            // speichert die Daten in einem stream...
        }
        public string RecoverData()
        {
            // lädt die Daten aus einem stream...
            return "stream stored data";
        }
        public void Dispose()
        {
            // schliesst die Ressource...
        }
    }


    public class SqlDataStoreProvider : System.Data.Common.DbConnection, IDataStoreProvider
    {
        private DataStoreController _Controller;

        public DataStoreController Controller
        {
            get { return this._Controller; }
        }
        public string Type
        {
            get { return "Sql"; }
        }

        public void Initialize(DataStoreController controller)
        {
            this._Controller = controller;
        }
        public void StoreData(string data)
        {
            // speichert die Daten in einer datenbank...
        }
        public string RecoverData()
        {
            // lädt die Daten aus einer datenbank...
            return "sql stored data";
        }
        public void Dispose()
        {
            // schliesst die Ressource...
        }
    }

}


in diesem beispiel hast du eine controller, der verschiedene provider zum datenspeichern und -wiederherstellen besitzt.
dies können x-beliebige klassen sein - sie müssen nur das notwendige interface 'IDataStoreProvider' implementieren, damit vorausgesetzt werden kann, dass sie über die benötigten methoden (welche das interface vorschreibt) verfügen, damit der controller mit ihnen arbeiten kann.
es gibt in diesem beispiel insgesamt 3 provider, die das interface implementieren.
diese klassen erben dabei von ganz unterschiedlichen klassen, und stellen somit völlig verschiedene funktionalitäten zur verfügung - eine klasse speichert in einem Stream, eine in einer Datenbank und die dritte in einer XmlDatei.
aber das interface setzt voraus, dass sie alle dieselben members implementieren müssen - und das ist halt die gemeinsame schnittstelle zwischen diesen ansonsten völlig unterschiedlichen klassen, die so eine gemeinsame dienstleistungsbasis zur verfügung stellen können.

und wie zu erkennen ist, braucht der controller überhaupt nicht zu wissen, mit wem er es zu tun hat - er weiss, dass die von ihm benötigten methoden vorhanden sind - auf welche art und weise die provider dann ihre arbeit erledigen, spielt für ihn keine rolle.

verstanden?!
😉

grtz
chief

@Chief Brodie

Wenn ich das jetzt richtig verstanden habe, dient ein Interface als Grundgerüst für ein bestimmtes vorgehen das man immer wieder hernehmen kann.

Hoffe das ich das jetzt richtig verstanden habe, denn wenn ich nach einer Klassenbezeichnung einen doppel Punkt gefolgt von einem Interface eingebe, bekomme ich angeboten die TAB Taste zu drücken. Und wenn ich das tu, werden alle Member dieses Interface in die Klasse eingefügt.

Bitte nicht schlagen wenn es falsch ist! 🙁

Gruss, GIJOE

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

sorry - aber ich gebs auf...

[edit]
doch - eines fällt mir noch ein.
nimm einen zug, ein schiff und ein flugzeug...
drei völlig verschiedene dinge, die nicht miteinander zu tun haben.
aber auch sie implementieren ein interface - nämlich, dass man sich a) reinsetzen kann, und b) sich damit forbewegen kann.
wie sie das anstellen, kann dir egal sein - hauptsache, du kommst bequem in den süden.
[/edit]

grtz
chief

@Chief Brodie

Für mich hört sich das an wie das erben von Oberklassen!

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

Ein Interface, ist wie der Name schon sagt, eine Schnittstelle. Es wird von der Implementierung abstrahiert. Das ist es, mehr nicht. Im Gegensatz dazu ist das Erben von einer Basisklasse gerade das Weitergeben vorhandener Implementation.

Stell dir eine gigantisch grosse Klasse vor, 10 Mb Code, 1 Gb Arbeitsspeicherverbrauch und 1 Tb lokale Daten. Eine Anwendung auf einem PDA könnte diese Klasse nicht instanziieren, geschweige denn verwenden.

Hat sie jedoch ein Interface, könnte man einen Proxy programmieren, der nur die Kommunikation mit der dicken Klasse übernimmt. Die Implementierung des Proxies ist eine völlig andere als die der dicken Klasse. Sie ist so verschieden, dass es keine gemeinsame Basisklasse gibt (bis auf Object). Nach außen stellt sich der Proxy aber wie die dicke Klasse dar ... weil beide das selbe Interface implementieren.

Gruss
Pulpapex

Für mich hört sich das an wie das erben von Oberklassen!

wo erben denn zug, schiff und flugzeug voneinander?!
das sind drei völlig verschiedene dinge!

ihre gemeinsamen berührungspunkte sind ein antrieb und die möglichkeit, personen oder fracht aufzunehmen - das realisieren sie aber alle auf völlig eigene art und weise:

  • der zug fährt auf rädern und ist elektrisch angetrieben
  • das schiff schwimmt und hat nen dieselmotor
  • das flugzeug fliegt und hat einen düsenantrieb

da ist nichts mit erben - wer denn von wem?

nein, sie haben die gemeinsamen bedingungen, dass sie sich forbewegen sollen und uns irgendwo hinbringen - das ist ihr interface.

und ein mensch weiss jetzt - aha, ich kann mich überall reinsetzen und werde irgendwohin gebracht - das ist dem menschen wichtig - wie die einzelnen geräte das anstellen, ist ihm völlig egal.

wenn du das für vererbung hälst, dann hast du vererbung ebenfalls nicht begriffen - sorry!

von vererbung könntest du evtl. in der reihe "kabinenroller > pkw > autobus" sprechen - die haben alle luftgefederte räder, fahren auf der strasse und besitzen einen kolbenmotor - nur beförderungsmenge, frachtkapazität und bequemlichkeit nehmen zu.

grtz
chief

@Chief Brodie

Zum Thema Erben!

Wenn ich z.B. eine Oberklasse habe die Spritverbrauch und Geschwindigkeit beinhaltet, dann kann doch das Flugzeug, das Schiff und die Bahn davon erben oder nicht?

Das mit dem Interface mus ich mir erstmal auf der Zunge zergehen lassen!

Aber an alle die den Versuch gestartet haben mir das verständlich zu machen, ein herzliches Dankeschön!

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

Hallo Chief Brodie,

sorry, bei Zug, Schiff und Flugzeug würde ich auch eher an eine gemeinsame Oberklasse (Fortbewegungsmittel) denken und als an Interfaces.

@GIJOE:
Aber versuchen wir es mal mit einer rein technischen Betrachtung: Zunächst wird sowohl durch Oberklassen also auch durch Interfaces etwas vererbt. Daher die Änhnlichkeit und daher vielleicht auch die Schwierigkeit, Interfaces zu verstehen. Nur werden durch (nicht abstrakte) Oberklassen Schnittstelle und Implementierung vererbt und durch Interfaces nur die Schnittstelle. Ein weiterer Unterschied ist, dass eine Klasse nur von einer Obklasse erben darf, aber von beliebig vielen Schittstellen.

herbivore

Wenn ich z.B. eine Oberklasse habe die Spritverbrauch und Geschwindigkeit beinhaltet, dann kann doch das Flugzeug, das Schiff und die Bahn davon erben oder nicht?

na, das ist aber arg konstruiert, würde ich sagen...

zumal die bahn in meinem beispiel elektrisch betrieben ist, also in der ursache ihrer bewegung auf induktion beruht...
...das schiff einen dieselmotor besitzt, dessen prinzip der rotierende kolbenmotor ist...
...und das flugzeug ein strahlentriebwerk hat, das auf linearem schub beruht.

ausserdem fliegt eines, das andere schwimmt und das dritte fährt über land.

ich kann nur schwer erkennen, wie man die drei in irgendeine vererbungsreihenfolge bringen könnte - sie haben evtl. irgendwo die gleiche ursprungsklasse "BewegtesObjekt"...
...aber sie implementieren alle "ITransportMittel", was die hauptmerkmale kennzeichnet.

grtz
chief

Nochmal auf den Punkt gebracht,

ein Interface ist die öffentliche Schnittstelle, das von anderen Klassen gesehen und verwendet wird. Dagegen geht es beim Ableiten ums Wiederverwenden einer internen Implementierung. Einmal geht es um die Sicht von außen und einmal um Interna.

Eine Klasse bietet natürlich auch eine öffentliche Schnittstelle. Benutzt man allerdings diese, hat man sich auch auf die Implementierung festgelegt. Das Konzept des Interface-Typs trennt die Schnittstelle von der Implementierung. Wird gegen ein Interface-Typ programmiert, wird die Implementierung austauschbar - siehe das Beispiel dicke Klasse/kleiner Proxy.

Durch Interfaces können verschiedene Klassen gleiche Funktionalitäten anbieten. Sie können dadurch von anderen Klassen auf die selbe Weise verwendet werden. Beispiele für Funktionalitäten sind das Sortieren mit ArrayList.Sort (IComparable) oder das Iterieren in einer foreach-Schleife (IEnumerable).

Gruss
Pulpapex

Ihr erwähnt immer das man anhand eines Interface eine Schleife durchlaufen kann.

Da ich im Moment an einem Brennerprogramm arbeite, und mir dieses von euch dargestellte Beispiel in diesem Programm siehe folgender C# Code aufgefallen ist. Glaub ich so langsam den Sinn eines Interface langsam zu verstehen.

Also:
Im ersten Teil instanziere ich eine Objektvariable "ndrivers" aus der Klasse "NEROLib.NeroDrivesClass"
Im zweiten Teil initialisiere ich diese Objektvariable mit allen gefundenen Brennerlaufwerke.

So und jetzt kommt´s, der dritte teil!
Um mich nicht wider zu blamieren, wäre ich dankbar wenn einer von euch mir erklärt was da jetzt genau von statten geht.

Was ich weis, ist das "ndrivers" eine Objektvariable ist und "NEROLib.NeroDrive (ndrive)" ein Interface.
Mit jedem foreach durchlauf holt das interface "ndrive", ein anderes Laufwerk aus ndrivers. Muss ich mir das jetzt so vorstellen das die Klasse "NEROLib.NeroDrivesClass" von dem Interface "NEROLib.NeroDrive" erbt, alle Members des Interfaces in der Klasse Implementiert sein müssen, und dadurch eben es ermöglicht wird anhand einer foreach Schleife alle Laufwerke ausgelesen zu können?

//Definition ndrivers
private NEROLib.NeroDrivesClass ndrives;

// Ermittle alle Laufwerke (Initialisierung)
ndrives = (NEROLib.NeroDrivesClass) neroclass.GetDrives(NEROLib.NERO_MEDIA_TYPE.NERO_MEDIA_CDR | NEROLib.NERO_MEDIA_TYPE.NERO_MEDIA_CDRW);

// Alle Ermittelten Laufwerke in der ComboBox anlegen
foreach(NEROLib.NeroDrive ndrive in ndrives)
{
// Laufwerkname in der ComboBox anlegen
cmb_brennerauswahl.Items.Add(ndrive.DeviceName);
}

Nicht schlagen! 🙁

Gruss, GIJOE

Auch Anfänger haben recht auf Leben! 😁

Ich versuche es noch mal am Beispiel eines Sotierers, auch um zu sehen, ob ich es verstanden habe.
Also, sortieren kann man ja fast alles. Egal ob Stringlisten, Zahlenarrays, Trees, Tabellen usw. Nun könnten die alle ihre eigenen Sortiermethoden besitzen oder von einer gemeinsamen Basisklasse abgeleitet sein, die das Sortieren erledigt. Das müsste dann aber wohl schon Object sein. Man kann auch eine Sortiererklasse schreiben, die beliebige Objekte, die einer gemeinsamen Basisklasse entspringen, welche die elementaren Methoden wie "vergleiche" und "tausche" bereitstellt, sortiert. Diese Methoden müssten dann natürlich in den abgeleiteten Klassen überschrieben werden.
Was aber, wenn man nun eine Klasse hat, die eben nicht von dieser Basisklasse abgeleitet wurde (aus welchen Gründen auch immer) und nun sortierbar werden soll? Eine andere Basisklasse "unterschieben" ist etwas heikel und eine eigene Implementierung des Sortieralgos in der Klasse ist unschön. Beruht das Ganze aber nicht auf einer Basisklasse, sondern auf einem Interface, so kann man einfach die Klasse ableiten und ihr das Interface verpassen.
In einfachen Klassen-Beispielen finde es auch ziemlich schwer die Notwendigkeit aufzuzeigen, auch weil man da ja alles in der Hand hat. Aber spätestens wenn man komplexe Klassen, die bereits mehrere Vererbungsstufen hinter sich haben, verwendet, wird die Sinnhaftigkeit klar.

Hallo josch,

Ich versuche es noch mal am Beispiel eines Sotierers, auch um zu sehen, ob ich es verstanden habe.

Ich denke du hast es verstanden.

Und genau: Bei der Oberklasse muss man sich für eine entscheiden, deshalb ist es schwierig oder unmöglich da noch die Sortieren-Basisklasse zwischenzubekommen. Interfaces kann man aber einer Klasse auf einer bebliebigen Ebene der Vererbungshierarchie beliebig viele verpassen.

herbivore

"im Zusammengang mit "Erklärungsbedarf Schnittstelle" ist mir noch eingefallen, dass Schnittstellen ursprünglich als Ersatz für Mehrfachvererbung in die OOP eingeführt wurden,"

Im leben nicht!!!.

"um die Probleme der Mehrfachvererbung zu umgehen."

Das stimmt jedoch, du wiedersprichst dir.

Schnittstellen wurden eingefürt um den Problem der reinen klassenbasierten vererbung vor zu beugen.
Sie sind im grunde die weiterentwicklung der rein klassenbasierten vererbung und eine weiterentwicklung der abstrakten klassen.

Wo bitte siehst du den den zusammenhang zwischen interfaces und mehrfach vererbung das einzige was da gemeinsam ist, ist das man bei beidem mehrfach verebung betreiben kann.

Interfaces haben keine implementierung. Und das ist eben der vorteil. Bei der mehrfachvererbung übernimmt man die methoden der klasse.
Nein also es gibt gravierende unterschiede.
Naja du kannst bei deiner Meinung bleiben die ich durch aus tolerie.
Aber wenn du recht hast 😁 dann müssten sehr viele Bücher die ich besitze neu geschrieben werden da sie falsche informationen verbreiten.
Oder würdest du sagen das COM seine konzepte auch mit Mehrfachvererbung verwirklichen könnte 😁

"Probleme entstehen immer dann, wenn verschiedene dieser Oberklassen (direkt oder indirekt) gleichnamige "Bestandteile", z.B. Instance-Variablen enthalten. Die Frage ist dann, ob der entsprechende Bestandteil einmal oder mehrfach in der Unterklasse enthalten ist, und wenn mehrfach, wie und wo auf welchen Bestandteil zugegriffen wird.

Dieses Problem mit der Mehrfachververerbung hat man immer, wenn die Oberklassen selbst eine gemeinsame Oberklasse haben, z.B. beif"

Das war nicht hauptgrund für interfaces.

Da Interfaces "nur" leere Hüllen sind, gibt es diese Probleme nicht. Wenn man mehrere Interfaces implementieren muss und dabei eine Namensgleichkeit auftritt, muss (und kann) man die entsprechende Methode oder Eigenschaft trotzdem nur einmal implementieren.

Naja man muss eigentlich nicht, in der klasse die die interfaces erbt können durchaus zwei methoden mit der gleichen signatur drin sein die auch zwei unterschiedliche implementierungen haben können.

Nur eben muss man bei der implementierung das interface angeben welches man meint:

public void ITest.func()

Man kann dann aber auch nur auf diese Funktion zugreiffen wenn man eine instance des interfaces macht also ein object der klasse kann auf die methode nicht zugreiffen das ist der nachteil.

Em darf ich fragen aus welchem Buch dein wissen über interfaces kommt. würde ich gerne wissen.

cya

Hallo Nostalb,

ich würde schon denken, dass meine Argumentation in sich schlüssig ist, wenn man unter Mehrfachvererbung versteht, dass das eine Klasse mehr als eine Oberklasse haben kann und man Interfaces nicht als Klassen (sondern eben als Interfaces) zählt.

In diesem Sinne erlaubt C# keine Mehrfachvererbung und bietet als (in gewissen Sinne besseren) Ersatz Interfaces.

Wir stimmen überein, dass Interfaces eingeführt wurden, um Problem der klassenbasierten (Mehrfach-)Vererbung vorzubeugen.

Wenn man Interfaces für einen kurzen Moment als eine spezielle Form von Klassen, nämlich im wesentlichen als pure abstract classes, betrachtet, sehe ich den Zusammenhang zwischen Mehrfachvererbung und Interfaces darin, dass die Angabe von mehreren Interfaces dann (eine Form der) Mehrfachvererbung wäre.

Das es zwischen Mehrfachvererbung im allgemeinen Sinne und Interfaces Unterschiede gibt, z.B. eben das Fehlen der Implementierungsvererbung, und dass Interfaces ein (in gewissem Sinne weniger leistungsfähiger) Ersatz für Mehrfachvererbung sind, ist ja kein Widerspruch.

Die Bücher müssen wegen mir natürlich nicht umgeschrieben werden. 🙂 Ich denke auch, dass ich mich nicht wirklich im Widerspruch zu ihnen befinde.

COM kenne ich nicht (genau genug), um darüber eine Aussage treffen zu können.

herbivore

PS: Mein Wissen über Interfaces stammt aus mehr als einem Buch. Das meine ich nicht überheblich, sondern nur so, dass ich dir jetzt nicht ein konkretes Buch nennen könnte, aus dem ich meine Aussagen direkt übernommen hätte.

Wenn man Interfaces für einen kurzen Moment als eine spezielle Form von Klassen, nämlich im wesentlichen als pure abstract classes, betrachtet, sehe ich den Zusammenhang zwischen Mehrfachvererbung und Interfaces darin, dass die Angabe von mehreren Interfaces dann (eine Form der) Mehrfachvererbung wäre.

Ja und genau an diesem punkt machst du einen gravierenden fehler!!!
Man sollte interfaces nicht verwenden um mehrfachvererbung zu betreiben.
Schließlich verwende ich interfaces auch in C++ obwohl es dort mehrfachvererbung gibt.
Und wann man es trotzdem macht ist es wahrscheinlich auf einen fehler im software design zurückzuführen.

Keine ahnung warum ich meine meinung so verträtte das liegt wahrscheinlich an meiner strikten trehnung der begriffe. Ich betrachte Interfaces nicht als eine art mehrfachvererbung. Ich habe auch in keiner meiner software interfaces als mittel zur mehrfachvererbung benutz.

Interfaces benutz man unter anderem um funktionalität während der laufzeit um zu tauschen.
Zum Beispiel gibt es eine klasse Search und diese hat eine instance des interfaces ISearch.

Nun kann man zwei eigene ISearch klassen machen die eine fürs internet und die andere für die platte.
Dan muss man nur noch der basis klasse eine instance einer dieser isearch klassen geben.

Ein Buch über Software engineering zum Beispiel macht diese strikte trehnung auch und so lange du das nicht tust begehst du einen Fehler.

Mehrfachvererbung ist ein Raubtier das nur von wenigen gezähmt werden kann. Für die Gurus ist es ein wertvolles Werkzeug für den rest der Welt bedeut es mehr probleme als nutzen.

Aber wie gesagt es kommt auf die ansichtsache an.
cya

Ohne jetzt wirklich alles im Detail gelesen zu haben einfach mal eine praktische Anwendung....

Mal angenommen ich will alle UI-Controls um eine Methode UpdateData erweitern....

Im Prinzip könnte man einfach alle Controls vererben und die Methode überall codieren.

Wenn man aber nun die Methode in einem Interface definiert und das Interface dann explizit oder implizit implementiert ist folgendes möglich


foreach (Control ctrl in this.Controls)
{
   IUpdateData iup=ctrl as IUpdateData;
   if (iup!=null)
   {
       iup.UpdateData();
       //dies funktioniert prima mit allen Controls die das Interface implementiert haben.... unabhängig davon was für ein control es ist
   }
}

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Vielleicht erhellt diese Sichtweise die Notwendigkeit oder EInsatzzweck von Interfaces:

Klassen modellieren sowohl die Daten eines Objektes (Member-Variablen), als auch deren Verhalten (Methoden).

Interface definieren nur reines Verhalten. Sie besitzen (oder speichern, wie man will), selbst keine Daten, sondern definieren ein Verhalten, welches Klassen, die sich von diesem Interface ableiten erfüllen müssen.

Beispiel: Du möchtest Dinge in deinem Programm druckbar machen. Dazu definierst du ein Interface namens IPrintable, welches vielleicht die Methoden Print() und Preview() definiert.

Diejeingen Klassen, deren Objekte druckbar sein sollen, müssen nun dieses Interface implementieren d.h. mit Leben füllen: Was wird gedruckt, wohin, etc.!

Der Vorteil liegt nun darin, dass die eigentlich Druckroutine im Hauptprogramm so aussehen kann:

foreach (IPrintable printobject in printlist)
{
printobject.Print();
}

Was das für Objekte sind ist egal, denn sie haben alle das gleiche Interface.

Das sieht erstmal ähnlich aus wie Vererbung (Polymorphie), ist es im Prinzip auch, aber die Denkweise dahinter ist eine andere. Klassische Objektorientierung hat einen stark datenzentrierten Charakter. Verhaltensaspekte stehen nicht so sehr im Fokus. Dies ist ein Problem, da gemeinsames, klassenübergreifendes Verhalten doch oft ein Thema ist (Drucken, Sortieren, etc.).

Die Frage ist nun: Wann Vererbung, wann Interfaces?

Dabei gilt: Vererbung, wenn Daten ein Rolle spielen, Interface wenn es um reines Verhalten geht. Klassen und Objekte bilden eher NOMEN ab (Rad, Mitarbeiter, Bestellung, etc.), Interfaces bilden VERBEN ab (Drucken, Sortieren, Freigeben, etc.).

Man sollte dazu sagen, dass der "Design-Trend" in den letzten Jahren in Richtung Interfaces geht. Vererbung wird zunehmend weniger eingesetzt, weil es die Klassen ziemlich starr koppelt. Interfaces können helfen, Aspekte von Objekten klarer voneinander zu trennen und zugleich zu dynamisieren. Dies ist wichtig, wenn man an Plug-In-Architekturen oder ähnliches denkt.

Hallo svenson,

gut beschrieben.

Nur ein sehe ich anders: Interfaces bilden ADJEKTIVE ab (druckbar, sortierbar, freigebbar, etc.). VERBEN stehen für Methoden (egal ob in Klassen oder Interfaces).

herbivore

Danke, svenson, jetzt hab ich das auch endlich mal kapiert, denke ich!

Jetzt mal eine Glaubensfrage: Ich werde bald damit beginnen, ein Programm für einen neuropsychologischen Test zu schreiben. Dabei möchte ich in einer DLL die entsprechenden benötigten Klassen kapseln.
Dabei dachte ich an:


          PATIENT
            /\
           /  \
    PAT.-M.     PAT.-OHNE

wobei "M." für "mit Medikation", "OHNE" für "ohne Medikation" steht.
An sich werden sie alle die gleichen Methoden und Eigenschaften besitzen. Nur dass die Patienten mit Medikation eben eine Liste der Medikamente/des Medikationsverlaufs haben werden. Ebenso soll es natürlich möglich sein, eine neue Medikation zu beginnen oder auch einen Patienten als "ohne Medikation" einzustufen, wenn er irgendwann einmal keine Medikation mehr erhält.
Was meint ihr, benötige ich hier irgendwo Interfaces oder kann ich hier ganz einfach vererben?
Ich würde jetzt eher letzteres denken, da es hauptsächlich um eine Datenerfassung und -auswertung sowie -präsentation geht, aber ich dachte mir, dass ich lieber mal frage, bevor ich alles mögliche neu machen muss...

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...

Ich würde da überhaupt keine Vererbung/Interfaces einsetzen. Ob ein Patient jemand "mit" oder "ohne" ist, ist nur ein zeitlich begrenzter Zustand. Ich würde grundsätzlich in die Patient-Klasse eine Liste mit Medikamentationen einbauen. Wenn diese leer ist, ist der Patient ohne Medikamentation, sonst mit, d.h.


public bool BekommtMedikamente {
  get { return medikamentationen.Count > 0; }
}

Das macht dein Programm einfacher. Drösel dein Informationssystem nicht zu weit auf. Sonst kriegst du nachher Klassen wie


public KassenPatientOhneMedikamenteMitNamenHorst : KassenPatientOhneMedikamenteDessenNameMitHBeginnt {
}

Du solltest also nicht für jeden möglichen Zustand eines Objektes eine eigene Klasse erstellen.

MfG VizOne

Original von VizOne
Das macht dein Programm einfacher. Drösel dein Informationssystem nicht zu weit auf. Sonst kriegst du nachher Klassen wie

  
public KassenPatientOhneMedikamenteMitNamenHorst : KassenPatientOhneMedikamenteDessenNameMitHBeginnt {  
}  
  

MfG VizOne

Fehlt da nicht noch der Name des Versicherers im Klassennamen 😉

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Hallo Schattenkanzler,

full ack mit VizOne.

Bei Klassennamen wie XmitY oder mit anderen "umständlichen" Namen sollte man ohnehin stutzig werden.

herbivore

Um das nochmal mit anderen Worten zu unterstützen:

Man stelle sich vor, der Patient wird eingeliefert und bekommt zunächst keine Medikamente. Dann wäre er "Objekt" der Klasse "PatientOhneMedikamente". Nun bekommt er welche. Er ist noch er gleiche Patient aber muss die Klasse wechseln.

Sowas ist bei OO immer falsch. Objekte "wechseln" nicht ihre Klassen, sondern ihre Zustände. Daher eben einfach als Property implementieren.

Sehr logsche Erklärung...manchmal habe ich das Gefühl, dass man durch die OOP ab und an etwas zu erzwingen versucht bzw. übermäßig detailliert arbeitet...na ja...
Dann gibt es eben nur eine simple Klasse Patient und dann is gut!

Danke euch!

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...

Original von svenson
Die Frage ist nun: Wann Vererbung, wann Interfaces?

Idealerweise kombiniert man diese sofern möglich.

Mal angenommen ich habe ein Interface IPersistent welches dazu dient den Zustand eines Objektes irgendwo zu speichern. Das Interface besteht aus zwei Methoden, welche irgendwo schon generisch existieren und bei der Interface - Implementierung lediglich aufgerufen werden (z.B: mit this-Reference als Parameter)....

Desweiteren stelle man sich vor, dass wir eine eigene ListBox und DataGrid haben, welche dieses Interface implementieren.

Wenn nun als nächste Anforderung kommt, dass 5 verschiedene UserControls gebaut werden müssen (und diese auch IPersistent implementieren sollen), dann macht es durchaus Sinn, eine BaseClass aus UserControl zu erstellen, IPersistent zu implementieren und diese Klasse als BaseClass für die 5 UserControls zu verwenden.

Vorallem im UI-Bereich kann man vieles gar nicht mit Vererbung lösen, da man ja unterschiedliche Controls vererbt... also verschiedene BaseClasses schon gegeben sind. Dann bleibt gar nichts anderes übrig, als Gemeinsamkeiten über Interfaces abzubilden.

Uebrigens habe ich sogar mal folgendes gemacht:

Ich hatte ein Interface zu fett gebaut. Die enthaltenen Methoden machten zwar absolut Sinn für mein Vorhaben. Für ein anderes Vorhaben hätte jedoch eine Teilmenge des Interfaces auch genügt. Um nun nicht x Members leer implementieren zu müssen (ist ja eh potenziell gefährlich) habe ich drei neue schmale Interfaces erstellt. Die Methoden auf die 3 Interfaces aufgeteilt und das ursprüngliche Interface so abgewandelt, dass es nun die 3 schmalen Interfaces implementiert.... So war die Abwärtskompatibilität sichergestellt, ein "Designfehler" plötzlich keiner mehr... und der Tag war gerettet.

BeispielCode (abstrakt)



	public class KannAlles:IEierlegendeWollmilchsauInterface
	{
		public KannAlles(){}
		#region IEierlegeInterface Members

		public void LegeEier()
		{
			// TODO:  Add KannAlles.LegeEier implementation
		}

		#endregion

		#region IGibWolle Members

		public void GibWolle()
		{
			// TODO:  Add KannAlles.GibWolle implementation
		}

		public void BloekeWieEinSchaf()
		{
			// TODO:  Add KannAlles.BloekeWieEinSchaf implementation
		}

		#endregion

		#region IKorrigiereAlleFehlerSelber Members

		public void KorrigiereAlleFehlerSelber()
		{
			// TODO:  Add KannAlles.KorrigiereAlleFehlerSelber implementation
		}

		#endregion
	}
	public interface IEierlegendeWollmilchsauInterface:IEierlegeInterface,IGibWolle,IKorrigiereAlleFehlerSelber
	{
	}

	public interface IEierlegeInterface
	{
		void LegeEier();
	}

	public interface IGibWolle
	{
		void GibWolle();
		void BloekeWieEinSchaf();
	}

	public interface IKorrigiereAlleFehlerSelber
	{
		void KorrigiereAlleFehlerSelber();
	}


Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

>Sehr logsche Erklärung...manchmal habe ich das Gefühl, dass man durch die >OOP ab und an etwas zu erzwingen versucht bzw. übermäßig detailliert >arbeitet...na ja...
>Dann gibt es eben nur eine simple Klasse Patient und dann is gut!
meine Frage:
Ist nun Sinn und Unsinn von Interfaces klargeworden oder nicht?
Ist Mehrfachvererbung(in dem Zusammenhang) klar oder nicht?
ok:waren 2 Fragen...

Zwei Fragen, eine Antwort: Jupp, denke schon...werde es in diesem Fall allerdings wohl wieder nicht wirklich benötigen, da ich die von mir angesprochenen Eigenschaften (wie Medikation) locker über Properties machen kann...simpel eigentlich...

Danke dennoch!

Greets - SK

Sagte ich schon Danke? Nein? ...kommt noch...

GIJOE
Schonmal daran gedacht dich selbst auf die Socken zu machen und danach zu suchen?