Braucht man Mehrfachvererbung



  • Habt ihr schon mal irgendwas realistisches programmiert, wo Mehrfachvererbung wirklich sinnvoll war?

    Findet ihr es besser, wenn z.B. eine GUI-Dialogklasse direkt ein Observer-Interface, für das Update einer Liste, implementiert (is-a) oder wenn die GUI-Dialogklasse eine andere Klasse enthält (has-a), die das Observer-Interface implementiert und dann die Liste updatet?



  • §561 schrieb:

    Findet ihr es besser, wenn z.B. eine GUI-Dialogklasse direkt ein Observer-Interface, für das Update einer Liste, implementiert (is-a) oder wenn die GUI-Dialogklasse eine andere Klasse enthält (has-a), die das Observer-Interface implementiert und dann die Liste updatet?

    Haengt immer von der Situation ab. In Java ist Mehrfachvererbung zB Standard. .NET geht einen anderen Ansatz ohne Interfaces. Es haengt halt idR von der Library ab was sie von dir erwartet. In C++ und C# wirst du deshalb eher weniger vererbung einsetzen als zB in java.



  • Ohne Mehrfachvererbung sind einige Probleme nicht OO-mäßig lösbar.

    Ein einfaches Beispiel wäre eine OO-Modelierung des folgenden Problems: Man hat einen unbewegten Ball, man hat einen hüpfenden Ball und einen Ball der seine Farbe ändert. Wie baut man in die Hierachie nun einen Ball ein, der hüpft und seine Farbe ändert?



  • rüdiger schrieb:

    Ohne Mehrfachvererbung sind einige Probleme nicht OO-mäßig lösbar.

    Ein einfaches Beispiel wäre eine OO-Modelierung des folgenden Problems: Man hat einen unbewegten Ball, man hat einen hüpfenden Ball und einen Ball der seine Farbe ändert. Wie baut man in die Hierachie nun einen Ball ein, der hüpft und seine Farbe ändert?

    Decorator?



  • man sollte zwischen mehrfachvererbung (nach c++ art) und implementierung mehrerer interfaces (nach java art) differenzieren. die java variante ist sehr sinnvoll, die c++ variante "echter" mehrfachvererbung überflüssig.



  • thordk schrieb:

    man sollte zwischen mehrfachvererbung (nach c++ art) und implementierung mehrerer interfaces (nach java art) differenzieren. die java variante ist sehr sinnvoll, die c++ variante "echter" mehrfachvererbung überflüssig.

    Ich sehe da keinen relevanten Unterschied.

    Ich muss ehrlich gesagt sogar gestehen, dass ich den Java Ansatz furchtbar finde. Die Idee ansich Interfaces und Klassen explizit zu trennen mag ok sein - aber ich kenne keine Sprache wo man Vererbung so oft einsetzt wie in Java.

    Man muss bei Vererbung IMHO 2 Sachen unterscheiden: man kann von konkreten Klassen erben und von Konzepten (oder wie es oefters genannt wird "Interfaces" - wobei damit nicht die Java Interfaces gemeint sind).

    Es spricht IMHO nichts dagegen wenn ein Konzept fertig implementierte Methoden anbietet. Beispiel "Serializable". Das Konzept Serializable darf ruhig serialize() defaultmaessig implementieren. Das macht die Vererbungshierachie nicht wirklich komplexer. Probleme bekommt man nur, wenn man von 2 konkreten Klassen erbt - zB BunterHuepfenderBall erbt von BunterBall und HuepfenderBall.

    Diese Vererbung ist dann nicht per se schlecht - aber wenn man von konkreten Klassen erbt, muss man aufpassen.



  • Ich finde Mehrfachvererbung (im C++ Stil) grauenhaft und versuche sie zu vermeiden wo es nur geht. Bis jetzt hatte ich auch noch nicht einen Fall, wo es nicht ohne sie ging.



  • mal davon abgesehen, dass mehrfachvererbung schnell zu problemen führen kann (uneindeutigkeiten oder im fall von c++ verdeckung von dekonstruktoren), sorgt sie imo auch ziemlich schnell für sehr eklige "interfaces". hatte mal nen gutes beispiel, bei dem jemand es für ne tolle idee hielt, ein superinterface für sensorenkontrolle zu schaffen, indem er zwei konkrete, inkompatible, implementierungen in einer klasse vereint und auch noch eine matrizenimplementierung dazugeklebt hatte. das hat dazu geführt, dass die doku ständig darauf hinweisen musste, welche methoden auf gar keinen fall nacheinander verwendet werden dürfen und, natürlich, dafür gesorgt hat, dass die anwendung ständig abgeschmiert ist, weil irgendwer die doku nicht gelesen hatte.

    wenn mehrfachvererbung nicht erlaubt wird, können solche grausamen konstrukte gar nicht erst entstehen.



  • Hi,

    Delphi, oder auch DIE typische objektorientierte Sprache Smalltalk kommen ohne aus. Also kanns nicht notwendig sein.
    In der Hand des Profis kanns das eine oder andere eleganter lösen, in der Hand des Anfängers wirds schnell ne Zeitbombe...

    Gruß Mümmel



  • Euer Fehler ist, dass ihr Mehrfachvererbung mit Mehrfachvererbung von konkreten Klassen gleich setzt. Ihr denkt bei Mehrfachvererbung sofort an:

    Fahrzeug
    Auto erbt von Fahrzeug
    Boot erbt von Fahrzeug
    AmphibienAuto erbt von Auto und Boot

    Aber das ist ein sehr sehr seltener Fall. Viel relevanter ist die Situation wo man von Konzepten erbt. Warum sollte ich nicht von nocopyable und Auto und Hashable erben können? Wo ist da auch nur ein funken Problem?

    Das Problem mit dem Interface Ansatz ist ja der, dass man plötzlich Code verdopplung hat. Man hat IFooable und AbstractFoo die beide komplett identisch sind, mit Ausnahme dass AbstractFoo eine implementierung besitzt. Warum kann aber IFooable nicht eine default implementierung annehmen?

    Genau diese Frage ist der Unterschied in der Mehrfachvererbung von Java und C++. In C++ kann man dann natürlich auch komplett blöde Sachen machen: Auto kann von Vogel, Boot, Flugzeug und std::vector erben. Aber der Punkt ist: man muss es nicht tun. Man erbt von Konzepten und nicht von konkreten Klassen. Eigentlich erbt man fast nie von konkreten Klassen...

    Lustigerweise sind Vererbungshierachien in C++ deshalb meistens viel schöner und flacher und vorallem weniger breit als in Java 😉



  • Shade Of Mine schrieb:

    Lustigerweise sind Vererbungshierachien in C++ deshalb meistens viel schöner und flacher und vorallem weniger breit als in Java

    das liegt aber eher daran, dass c++ user davor zurückschrecken und gleich 'designfehler' und laufzeitprobleme wittern, wenn mal geerbt wird oder gar 'virtual' zum einsatz kommt.
    🙂



  • java-freak schrieb:

    Shade Of Mine schrieb:

    Lustigerweise sind Vererbungshierachien in C++ deshalb meistens viel schöner und flacher und vorallem weniger breit als in Java

    das liegt aber eher daran, dass c++ user davor zurückschrecken und gleich 'designfehler' und laufzeitprobleme wittern, wenn mal geerbt wird oder gar 'virtual' zum einsatz kommt.
    🙂

    Keine Frage - das ist eben das Lustige das passiert wenn Paradigmen übernommen werden ohne sie zu verstehen.

    In Java hat man viel Vererbung, Laufzeitpolymorphie wohin man nur schaut - in C++ dagegen fürchtet man sich fast vor einem virtual.

    Es gibt viele solche lustige Erscheinungen - das bedeutet aber nicht dass die Sprache deshalb schlecht sei 😉



  • Also ich benutze dann virtual, wenn ich es brauche. Nicht mehr und nicht weniger.



  • Artchi schrieb:

    Also ich benutze dann virtual, wenn ich es brauche. Nicht mehr und nicht weniger.

    Gute Programmierer programmieren gut 😉

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.



  • Shade Of Mine schrieb:

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.

    Ja, nur in Java hast du nicht sowas wie Functors. Nennt sich in anderen Sprachen glaube ich Delegate? In Java bleibt dir somit nichts anderes übrig als das Listener-Interface zu "vererben".

    Und natürlich finde ich std::tr1::function toll, dann brauche ich keine Vererbung, um eine beliebige (Member-) Function zu verbinden (std::tr1::bind). Ein virtual kann man so natürlich einsparen.

    struct Observer
    {
        void update();
    };
    
    Observer obs;
    std::tr1::function<void (void)> obs_fun = std::tr1::bind(&Observer::update, &obs);
    
    // später:
    obs_fun();  // ruft  obs.update() auf
    

    obs_fun kann natürlich jede andere beliebige (Member-)Function binden.

    EDIT: function und bind kommen auch in C++0x rein, und somit direkt im Namespace std.



  • Shade Of Mine schrieb:

    Es ist lediglich interessant sich durchschnittlichen oder eher schwächeren Code anzusehen. Da strotzt Java vor Vererbung und 100.000 Listener die überall hingeerbt werden und in C++ hat man dagegen nur ein paar functors die durch die Gegend fliegen.

    Sehr objektiver Sprachenvergleich. 👍 🤡



  • Artchi schrieb:

    Ja, nur in Java hast du nicht sowas wie Functors. Nennt sich in anderen Sprachen glaube ich Delegate? In Java bleibt dir somit nichts anderes übrig als das Listener-Interface zu "vererben".

    Schlechter Java Code:

    public class Frame1 extends JFrame implements AListener, BListener, CListener, DListener... {
    ...
      foo.registerFooListener(this);
      bar.registerBarListener(this);
    }
    

    Guter Java Code:

    public class Frame1 extends JFrame {
    ...
      foo.registerFooListener(new FooListener() {
        ...
      });
      bar.registerBarListener(new BarListener() {
        ...
      })
    
    }
    

    aber scheinbar sind wir noch nicht reif genug um über praktiken zu sprechen die sich eingebürgert haben...



  • Du hast Anonyme Klassen... schön, habe ich hier in meinen Projekten auch. Trotzdem ist es Vererbung! Nur das am Ende die spezielle Implementierung keinen Namen hat. Du hast das Interface-Vererben-Problem nicht umgangen, nur verlagert. Von den Interface bin ich immer noch abhängig und Polymorphy ist es trotzdem noch!



  • Artchi schrieb:

    Du hast Anonyme Klassen... schön, habe ich hier in meinen Projekten auch. Trotzdem ist es Vererbung! Nur das am Ende die spezielle Implementierung keinen Namen hat. Du hast das Interface-Vererben-Problem nicht umgangen, nur verlagert. Von den Interface bin ich immer noch abhängig und Polymorphy ist es trotzdem noch!

    Habe ich je gesagt dass das das Problem ist?

    Das Problem sind tiefe und breite Vererbungshierachien.



  • Das grundsätzliche Problem bei der Implementation der Mehrfachvererbung ist doch die Relativität der Objektadresse. Als unmittelbare Konsequenz sind C++-Methodenzeiger für Delegate-Zwecke nahezu unbrauchbar, wie Don Clugston anschaulich darlegt. Auch laufen dadurch sich ergebende Unausweichlichkeiten im Umgang mit Objektzeigern dem intuitiven Verständnis des Programmcodes zuwider. Wie sollte man auf die Idee kommen, daß eine harmlose Anweisung wie

    Base* b;
    Derived* d;
    ...
    b = d;
    

    die Zeigeradresse verändert? Und gesetzt, Derive leite von mehreren Klassen ab, die ihrerseits von Base abstammen, was bei polymorphielastiger Programmierung vorkommen kann: mit welch überflüssiger Verzweiflung reagiert ein Novize auf die dröge Verweigerung des Compilers, dasselbe Stück Codes zu übersetzen?

    Mit Interfaces im herkömmlichen Sinne - so wie sie auch in Delphi und vermutlich in Java existieren - wird dieses Problem vermieden, da sie keine Datenelemente enthalten. Das hat sich in vielen Situationen, ob nun Java, .NET oder COM, als sinnvoll erwiesen (wenngleich in Java ein gewisses Überreizungspotential bestehen mag). Nach einer praktischen und alternativlosen Anwendung der Mehrfachvererbung von C++ jedoch suche ich noch immer vergeblich.


Anmelden zum Antworten