Abstrakte Klassen als Schnittstellen



  • Hi, ich habe schon oft von Abstrakten Klassen als Schnittstellen gehört, wie funktioniert das?
    Ich vermute man Definiert eine A.Klasse in der Header Datei und einen Abkömling von ihr in der Implementierungs Datei.
    Danach kann man einfach per Polymorphie mit einen Pointer auf die A.Klasse arbeiten, ohne dass der "User" selbst Objekte der Schnittstelle instanzieren kann,
    liege ich da halbwegs richtig oder bin ich völlig auf dem Holzweg?



  • Ich vermute man Definiert eine A.Klasse in der Header Datei und einen Abkömling von ihr in der Implementierungs Datei.

    Nee, nicht ganz. Man definiert eine Abstrakte Klasse in einer Header-Datei. Und einen Abkömmling auch in einer Header-Datei und ggf. zusätzlicher .cpp Datei.

    Quasi so:

    //basis.h
    
    class Base {
      public:
        virtual void do_sth() = 0; //Pure-Virtual Das heißt keine Objekte möglich
    };
    
    //derived.h
    
    class Derived : public Base {
      public:
        virtual void do_sth(); //Die Methode wird überschrieben -> Objekte möglich
    }
    
    //derived.cpp : Implementierung von Derived::do_sth() { ... }
    

    Der User kann jetzt Objekte von Derived erstellen und diese auch über den Basisklassenzeiger verwenden:

    void do_kA(Base *ptr) { ... }
    

    do_kA könntest du jetzt als Paramter einen Pointer auf ein Derived Objekt geben. Wenn es eine 2te Klasse gäbe die von Base erbt könnte diese ebenfalls als Parameter übergeben werden.

    Edit: Du kannst aber auch alles in eine Datei packen oder kA wie.. 😉
    Falls base noch ein paar Methoden haben soll die nicht pure virtual sind kannste da selbstverständlich auch ne base.cpp anlegen.



  • Danke erstmal, aber was hat das jetzt mit Interfaces zu tun?
    Der Zusammenhang leuchtet mir noch nicht so ganz ein.

    ps.: Bis jetzt habe ich immer Namespaces verwendet, aber da fehlen mir einfach wichtige Sprachmittel wie Vererbung etc.



  • Eigentlich hat das gar nichts mit Schnittstellen zu tun.

    Von einer abstrakten Klassen ist es nicht Möglich eine Instanz zu erzeugen. Man muß von ihr eine konkrete Klasse ableiten. Dabei wird dann gefordert das die konkrete Klasse alle Methoden der abstrakten Klasse implementiert.
    Somit sind alle Methoden der Oberklasse vorhanden und man kann sie benutzen.

    Jede abgeleitete Klasse kann dabei Polymorphie vorwenden.



  • Namespaces sind nur dazu da um Namenskollisionen zuvermeiden bzw. um den Code ggf. ein wenig zu ordnen.

    Base ist doch die Schnittstelle? Schnittstellen werden benutzt um gleiche Aktionen auf unterschiedliche und dennoch irgendwo gleiche Objekte zuermöglichen.
    Als Beispiel:

    class Baum {
      public:
        virtual void saegen() = 0; //Alle Bäume können gesägt werden ;)
    };
    
    class Eiche : public Baum {
      public:
        virtual void saegen() { //Saege die Eiche ... }
    };
    
    class Ahorn : public Baum {
      public:
        virtual void saegen() { //saege den Ahorn ... }
    };
    

    Wenn Ich jetzt eine Funktion habe:

    void wald_abholzen(Baum * baum) { 
      baum->saegen();
    }
    

    Diese Funktion erwartet als Parameter einen Baum, also konkrete Objekte der Unterklassen Eiche oder Ahorn, da Baum pure virtual ist.

    Baum *eiche = new Eiche;
    Baum *ahorn =new Ahorn;
    wald_abholzen(eiche); //eiche->saegen wird aufgerufen
    wald_abholzen(ahorn) //ahorn->saegen wird aufgerufen
    

    PS: Ich hoffe dass das deine Frage beantwortet.. 😉



  • frosty schrieb:

    Eigentlich hat das gar nichts mit Schnittstellen zu tun.

    Von einer abstrakten Klassen ist es nicht Möglich eine Instanz zu erzeugen. Man muß von ihr eine konkrete Klasse ableiten. Dabei wird dann gefordert das die konkrete Klasse alle Methoden der abstrakten Klasse implementiert.
    Somit sind alle Methoden der Oberklasse vorhanden und man kann sie benutzen.

    Jede abgeleitete Klasse kann dabei Polymorphie vorwenden.

    Fast, IMHO müssen nur alle abstrakten Methoden überladen werden.

    Ich bin mir darüber im klaren, was Abstrakte Klassen sind und wie man sie erstellt,
    nur hatte ich öfter von "TITEL" gehört, daher die Frage.
    Mit Schnittstellen/Interfaces meinte ich eigentlich eher die Programmierschnittstelle von Modulen.

    Namensbereiche kann man im übrigen auch als eine art Ersatz für Klassen verwenden, wenn nicht mehr als ein Objekt dieser Klasse verwendet wird.



  • roan312 schrieb:

    Hi, ich habe schon oft von Abstrakten Klassen als Schnittstellen gehört, wie funktioniert das?
    Ich vermute man Definiert eine A.Klasse in der Header Datei und einen Abkömling von ihr in der Implementierungs Datei.
    Danach kann man einfach per Polymorphie mit einen Pointer auf die A.Klasse arbeiten, ohne dass der "User" selbst Objekte der Schnittstelle instanzieren kann,
    liege ich da halbwegs richtig oder bin ich völlig auf dem Holzweg?

    Eine Schnittstelle definiert welche Methoden zur Verfügung gestellt werden müssen. Wie eine Schnittstelle implementiert ist interessiert den Verwender nicht, er wählt eine passende oder noch besser: Lässt sie erstellen (Factory).

    Beispiel: Team A muss auf Lib von Team B zugreifen, bereits am Anfang des Projktes muss die Schnittstelle geplant sein damit alles schön hinhaut. Nun wurde alles durchgeplant und die Schnittstelle steht, bloß hat Team B genausowenig wie Team A Code in dieser Zeit geschrieben. Also erstmal nur die Schnittstelle bauen:

    class Interface
    {
        virtual void foo () = 0;
        virtual void bar () = 0;
    };
    
    Interface* CreateInterface ();
    

    Team A benötigt macht also einstweilen nur folgendes:

    Interface* myInterface = CreateInterface();
    

    Damit das Alles auch debugbar wird erstellt Team B erstmal eine Dummy-Implementierung:

    class Dummy : public Interface
    {
        void foo () { cout << "FOO" << endl; }
        void bar () { cout << "BAR" << endl; }
    };
    
    Interface* CreateInterface ()
    {
        return new Dummy();
    }
    

    Sobald das beendet ist können beide Teams seelenruhig an ihrem Projekt arbeiten, soll nun die Dummy-Version durch eine neue ersetzt werden muss sich Team A drum nicht kümmern: B ändert einfach die Implementierung:

    Interface* CreateInterface ()
    {
        return new MostPowerfulInterfaceImpl();
    }
    

    Die Technik die für Schnittstellen in C++ verwendet wird sind Abstrakte Klassen + Polymorphismus. Die Idee dahinter bleibt aber in allen Sprachen: Implementierung von Schnittstelle trennen.

    MfG SideWinder



  • Wenn Du das weißt, hast Du die Antwort auf Deine Frage schon gewußt. Also war die Frage unsinnig.
    Eben durch ableiten einer abstrakten Klasse kannst Du die Form von konkreten Implementierungen festlegen. Also genau das was Schnittstellen auch tun.



  • Danke Sidewinder, aber noch zwei Fragen:
    Wieso soll die erbende Klasse in der Header-Datei stehen?
    Sie hat Team-B doch gar nicht zu interessieren.

    Und wozu braucht man dafür die A.Klasse?
    T.A. kann doch sofort die Schnittstelle der "fertigen" Klasse vorlegen
    und dann Stück für Stück die Implementierung erzeugen. (?)



  • frosty schrieb:

    Wenn Du das weißt, hast Du die Antwort auf Deine Frage schon gewußt. Also war die Frage unsinnig.
    Eben durch ableiten einer abstrakten Klasse kannst Du die Form von konkreten Implementierungen festlegen. Also genau das was Schnittstellen auch tun.

    War sie nicht, ich habe gefragt wie man sie am besten einsetzt, nicht wie man sie erstellt.



  • Als Tip:

    Wenn Du dich genauer informieren willst, dann wirf mal einen Blick in die "Bibel" der Software-Technik von Helmut Balzert.



  • roan312 schrieb:

    Hi, ich habe schon oft von Abstrakten Klassen als Schnittstellen gehört, wie funktioniert das?

    Da steht aber was anderes. 🙂



  • Stimmt, ich habe mich später aber noch präzisiert. 😃



  • ok, zusammenfassend:

    irgendwie Beide unpräzise.

    Vielleicht sollten wir beide das mal üben: Ich lesen; Du formulieren.

    😃 😃 😃

    Ciao, schönen Abend



  • Von mir aus... 🙂

    Aber um auf meine Frage zurück zu kommen:

    Und wozu braucht man dafür die A.Klasse?
    Team-A kann doch sofort die Schnittstelle der "fertigen" Klasse vorlegen
    und dann Stück für Stück die Implementierung erzeugen.



  • Interface wird von B bereitgestellt, die Dummy-Impl und die tolle Impl stammen beide von Team B und sind vorzugsweise in einer anderen Header-Datei gut aufgehoben (am besten alles rums in eine DLL). Team A hat noch gar keine Klasse in meinem Beispiel, alles was von denen stammt ist ihr Code um eine Schnittstelle zur Lib zu bekommen:

    Interface* teamA_kann_jetzt_lib_benutzen = CreateInterface();
    

    Oder hast du ganz einfach meine beiden Teams umgedreht, dann lautet die Antwort zu:

    Und wozu braucht man dafür die A.Klasse? 
    T.A. kann doch sofort die Schnittstelle der "fertigen" Klasse vorlegen 
    und dann Stück für Stück die Implementierung erzeugen. (?)
    

    A hat nun eine Dummy-Impl abgeschlossen die mehr schlecht als recht ist. Jetzt ist aber bar() auf das Ergebnis von foo() angewiesen or something like that, da kann ich nicht hergehen und die tolle Impl erst einmal nur in foo() reinschreiben weil bar() dann nicht mehr funktioniert und bar2() auch nicht und überhaupt dann alles erstmal kaputt ist.

    Außerdem kann ich so schön versionieren, per Parameter kann ich an CreateInterface vielleicht noch übergeben ob ich eine optimierende version will oder eine standard-version, weil die neue optimierende version nur auf den neuen rechnern läuft die kaum jemand hat. In jede Methode ne if-Abfrage welche version? ekelhaft

    Ja und dann gibts noch 100 weitere Vorteile die du dir alle reinziehen kansnt wenn du dir zB ein büchlein über COM kaufst, die haben das dort exzessiv benützt.

    MfG SideWinder



  • Vielen vielen Dank SideWinder, jetzt hab ich den Sinn der Sache verstanden.


Anmelden zum Antworten