Braucht man Mehrfachvererbung



  • Shade Of Mine schrieb:

    byto schrieb:

    @Bashar: Jo, Java-Interface-Implementierung ist im Grunde ja nichts anderes als Vererbung. Aber man liest halt immer wieder als Kritik am Interface-Konzept von Java, dass man damit Code duplizieren muss. Und das ist halt schlicht falsch, wenn mans auf oben beschriebenem Weg macht.

    Wie genau komm ich an IFooable und AbstractFoo vorbei?

    Du kommst damit um die Vererbung im herkömmlichen Sinne vorbei, also mittels extends. Genau das kann nämlich böse werden und zu unwartbarem Code führen, wenn man irgendwann unüberschaubar viele Vererbungshierarchien hat.

    Natürlich hat man weiterhin Interfaces und implementiert diese, was prinzipiell auch ne Form von Vererbung ist. Aber es wird hier ja hoffentlich niemand ernsthaft daran zweifeln, dass es sinnvoll ist, hin zu Schnittstellen zu programmieren.



  • audacia schrieb:

    Dies scheint legales C++ zu sein, jedenfalls kompilieren Comeau, MSVC und BCC das anstandslos (MSVC ist wenigstens so entgegenkommend und warnt vor möglichen Konsequenzen). Ob aber der Wert von i nun erwartungsgemäß 42 oder aber undefiniert ist, hängt schlicht davon ab, in welcher Reihefolge die Basisklassen von Derived genannt werden.

    char* p=0;
    *p=0;

    ist auch legal weil es kompiliert - aber so ganz definiert ist das verhalten dann doch nicht.

    Falls du meinst, so etwas tue man doch nicht: Erstens wird fast alles, was der Compiler erlaubt, auch getan, und zweitens tut Microsoft das höchstselbst in den MFC.

    Man tut es nicht und wenn man es tut, dann weil man die konsequenzen kennt.

    C++ verbietet es einem nicht in den fuss zu schiessen.

    Du machst es dir aber sehr einfach damit, meine Argumentation ad absurdum zu führen 😉

    Weil die Argumentation absurd _ist_. Mehr feature will man immer haben aber _irgendwo_ muss eine grenze gezogen werden. man kann nicht _alles_ haben. ist einfach ein fakt.

    Zum Zeitpunkt der Verabschiedung des C++-Standards waren Delegates immerhin so gefragt, daß Borland sich schon für die OWL mit einer Compilererweiterung behelfen mußte und Qt das präprozessorgestützte Signal/Slot-System einführte, um genau diesem Mangel beizukommen. Und vor 1998 waren die meisten Compiler ohnehin nicht sattelfest genug, um all die templatebasierten Workarounds, die in der Theorie möglich und heute in Boost implementiert sind, verstehen zu können.

    Selbiges fuer Threads und 100.000 andere Sachen. Man kann nicht alles haben, ist einfach so.

    Solltest du weiterhin daran zweifeln, so empfehle ich dir dringend, oben bereits verlinkten Artikel von Don Clugston zu lesen.

    kenn ich schon

    Shade Of Mine schrieb:

    der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.

    Nein.

    Was genau koennen die C++ lambda ausdruecke nicht was du von einem closure erwartest?

    Ich fragte dich nach Mehrfachvererbung, nicht nach der Implementation mehrerer Interfaces. Hättest du meinen vorletzten Post genau gelesen, hättest du feststellen können, daß ich ausdrücklich dazwischen unterscheide:

    Und ich sehe den Unterschied einfach nicht. Ein Interface ist ein anderer Name fuer ein Konzept.

    Manchmal hilft es nicht dem Marketing zu vertrauen sondern sich tiefer mit den Konzepten zu befassen. Es ist einfach keine genuegende Definition zu behaupten dass ein Interface keine logik enthalten duerfen.

    Das mag fuer einzelne Sprachen ja stimmen, aber eben nur fuer die Implementierung einzelner Sprachen. Nicht im grossen Kontext. Ein Interface ist eine Schnittstelle - ein Konzept. Warum sollte ich nicht ein "Interface" nehmen koennen und logik einbauen. Dadurch aendert sich das Konzept ja kein bisschen.

    Der Grund warum manche Sprachen Interfaces anbieten hat technische Gruende. Man tut sich naemlich schwer sonst von einer root Klasse ableiten zu koennen ohne dauernd deadly diamonds of death zu bauen...

    Shade Of Mine schrieb:

    Aber was das wirkliche Problem ist, ist das erben von konkreten Klassen.

    Welches Mehrfachvererbung heißt 😉

    Und das ist eben nur zum Teil richtig.

    Mehrfachvererbung in c++:

    class Test : private boost::nocopyable, public std::vector, hashable {
    };
    

    Es ist mehrfachvererbung aber ich erbe nur von einer konkreten Klasse.

    hashable ist kein interface im Java sinn - es enthaelt zB die fertig implementierte non virtual methode "hash" die anhand der adresse des this Zeigers einen hashwert liefert.

    Aber in meinen Augen ist hashable (vom Sinn jetzt mal abgesehen) keine konkrete Klasse aber ebenfalls auch kein konkretes Interface (nocopyable bietet zB garkeine schnittstelle an).

    wenn nun hashable den hash wert (warum auch immer) zwischenspeichert, dann habe ich nicht nur codeelemente sondern auch datenelemente in hashable. definitiv kein java interface mehr. fuer mich ist es aber immernoch ein Konzept.

    Daß eine Vielzahl an Schlüsselwörtern eine schlechte Sprache ausmacht, ist ein Vorurteil von C- und C++-Programmierern.

    Und was wenn ich kein C- oder C++ Programmierer bin?
    Es ist eine Erfahrung die ich mit einer Menge Sprachen gemacht habe - das hinzupatchen von features ueber keywords ist meistens unpraktisch.

    Eine gute Sprache bietet eine kleine Basis anhand derer ich alle moeglichen konstrukte bauen kann. c++ bietet zB sehr doofe keywords wie zB register oder auto. Und vorallem was recht furchtbar ist, ist das wiederverwenden eines keywords in einem komplett anderen kontext. zB static oder typename. Java hat zB ein sehr schoenes set an keywords.



  • byto schrieb:

    Shade Of Mine schrieb:

    byto schrieb:

    @Bashar: Jo, Java-Interface-Implementierung ist im Grunde ja nichts anderes als Vererbung. Aber man liest halt immer wieder als Kritik am Interface-Konzept von Java, dass man damit Code duplizieren muss. Und das ist halt schlicht falsch, wenn mans auf oben beschriebenem Weg macht.

    Wie genau komm ich an IFooable und AbstractFoo vorbei?

    Du kommst damit um die Vererbung im herkömmlichen Sinne vorbei, also mittels extends. Genau das kann nämlich böse werden und zu unwartbarem Code führen, wenn man irgendwann unüberschaubar viele Vererbungshierarchien hat.

    Kapiere ich nicht.
    Warum ist es besser von IFooable zu erben und den Code von AbstractFoo::doFoo() zu copy&pasten als direkt von AbstractFoo zu erben? Welchen Vorteil bringt mir das?

    Mir passiert das hin und wieder und ich aergere mich ueber sowas. Auch dass ich dauernd 2 Klassen (also 1 abstracte klassen und 1 interface) implementieren muss nur um eine defaultimplementierung anbieten zu koennen.

    Erklaer mir mal genau warum die Welt untergeht wenn IHashable eine defaultimplementierung fuer hash() anbietet.

    Wie ich schon gesagt habe: niemand will von vector, Auto, Haus und string ableiten. Aber ich will von Hashable, nocopyable, Serializable,... erben koennen. Der Punkt ist nur, dass meine Definition eines Konzeptes nicht mit der Definition eines Java Interfaces kompatibel ist. Genau das ist mein Problem.

    Ich denke Java Interfaces sind einfach zu restriktiv. Es ist natuerlich super weil man damit gleich verbietet von vector, Auto, Haus und string zu erben - aber es bringt probleme mit sich. Naemlich dass man ploetzlich Code verdopplung hat (man muss jedesmal AbstractFoo und IFooable erstellen) und man macht Copy&Paste vom AbstractFoo Code wenn man IFooable verwenden muss.

    Wo ist denn der eigentliche Unterschied zwischen AbstractFoo und IFooable - der Unterschied ist nur technischer Natur dass IFooable keine default implementation anbietet. Es gibt keine logische Trennung. Deshalb sehe ich diese Trennung nicht ein.



  • Shade Of Mine schrieb:

    char* p=0;
    *p=0;

    ist auch legal weil es kompiliert - aber so ganz definiert ist das verhalten dann doch nicht.

    Dein Vergleich ist inadäquat. Wie ich schrieb, hängt es in meinem Beispiel davon ab, ob und in welcher Reihenfolge Mehrfachvererbung angewandt wird. Für Einfachvererbung oder aber für die erste Basisklasse scheint das sowohl definiert als auch sinnvoll zu sein. Das ist einer der Gründe, der zumindest gegen die C++-Version von Mehrfachvererbung (einigen wir uns doch auf den eindeutigeren Begriff der Mehrfach-Klassenvererbung) spricht.

    Shade Of Mine schrieb:

    Man tut es nicht und wenn man es tut, dann weil man die konsequenzen kennt.

    Ja, genau 🙄

    Shade Of Mine schrieb:

    Weil die Argumentation absurd _ist_. Mehr feature will man immer haben aber _irgendwo_ muss eine grenze gezogen werden. man kann nicht _alles_ haben. ist einfach ein fakt.

    Schön, daß du dich in der Lage siehst, objektiv über die Absurdität meines Standpunktes urteilen zu können. Meine Argumente habe ich genannt; jeder bilde sich selbst seine Meinung. Let's agree to not agree.

    Shade Of Mine schrieb:

    Shade Of Mine schrieb:

    der neue c++ standard hat closures. wie das implementiert wird, ist komplett egal.

    Nein.

    Was genau koennen die C++ lambda ausdruecke nicht was du von einem closure erwartest?

    Meine Reaktion bezog sich auf __closure aka Delegates. Entschuldige die Mißverständlichkeit.

    Deine Argumente für die Mehrfachvererbung kann ich nachvollziehen. Doch scheint mir ihre Verwendung einerseits keineswegs unabdingbar, und andererseits wiegen die Probleme meiner Auffassung nach wesentlich schwerer.

    Shade Of Mine schrieb:

    Eine gute Sprache bietet eine kleine Basis anhand derer ich alle moeglichen konstrukte bauen kann. c++ bietet zB sehr doofe keywords wie zB register oder auto. Und vorallem was recht furchtbar ist, ist das wiederverwenden eines keywords in einem komplett anderen kontext. zB static oder typename. Java hat zB ein sehr schoenes set an keywords.

    Wie du feststellst, ist es durchaus grausam, ein Schlüsselwort kontextabhängig unterschiedlich zu interpretieren - so wie C++ das, um die Einführung eines neuen Schlüsselwortes zu vermeiden, mit äußerster Verzweiflung tut. In Sprachen wie Delphi oder meinetwegen auch Java finde ich das viel entspannter.



  • audacia schrieb:

    ...ist es durchaus grausam, ein Schlüsselwort kontextabhängig unterschiedlich zu interpretieren - so wie C++ das, um die Einführung eines neuen Schlüsselwortes zu vermeiden, mit äußerster Verzweiflung tut. In Sprachen wie Delphi oder meinetwegen auch Java finde ich das viel entspannter.

    java kriegt's sogar hin, keywords trotz mehrfacher verwendung (final) so einzusetzen, dass die bedeutung so ähnlich ist, dass jeder es sofort versteht, wenn er nur einen fall kennt. das ist bei C (extern und static z.b.) weniger gut gelungen.
    🙂



  • audacia schrieb:

    camper schrieb:

    Gegenfrage: warum sollte man sich überhaupt irgendwelche Gedanken über Adressen machen müssen?

    Schreibt der Standard vor, daß static_cast <Base*> (reinterpret_cast <Derived*> (0)) == 0 ist, auch wenn Base nicht am Beginn des Objektlayouts von Derived liegt? Falls nicht, müßte man p ? static_cast <Base*> (p) : 0 schreiben.

    Der Standard schreibt hinsichtlich reinterpret_cast so gut wie überhaupt nichts vor, sofern die betreffenden Typen (wie in unserm Falle) keine PODs sind und auch nicht union-Member sein können. Das trifft so auch auf einfache Vererbung zu. Ich wiedeehole meine Frage: warum sollte man so etwas tun wollen? Praktisch alle Szenarien, in denen man das brauchen könnte, sind noch aus anderen Gründen undefiniert. Und ein Sprachfeature (Mehrfachvererbung) für schlecht zu halten, weil damit bestimmte andere furchtbare Sachen nicht richtig funktionieren, ist nicht sehr überzeugend.

    Ein anderer, wesentlich unangenehmerer Aspekt im Zusammenhang mit Methodenzeigern: seltsamerweise darf (oder wenigstens: sollte) man einem Methodenzeiger für eine Basisklasse keine Methode einer abgeleiteten Klasse zuweisen. Tut man es doch (oder erzwingt es mittels Cast), so passieren haarsträubende Dinge:

    class Base
    {
    protected:
        int i;
    
      public:
        Base (void) : i (42) {}
        int func(void) { return i; };
    };
    typedef int (Base::* basemember_t) (void);
    
    class Base2
    {
        int j;
    
      public:
        Base2 (void) : j (1337) {}
    };
    
    class Derived : public Base2, public Base
    {
      public:
        int new_func(void) { return i; };
    };
    
    void foo (void)
    {
      Derived d;
      int i;
      basemember_t m = static_cast <basemember_t> (&Derived::new_func);
      i = (d.*m) ();
    }
    

    Dies scheint legales C++ zu sein, jedenfalls kompilieren Comeau, MSVC und BCC das anstandslos (MSVC ist wenigstens so entgegenkommend und warnt vor möglichen Konsequenzen). Ob aber der Wert von i nun erwartungsgemäß 42 oder aber undefiniert ist, hängt schlicht davon ab, in welcher Reihefolge die Basisklassen von Derived genannt werden.

    Falls der Compiler alles richtig macht, spielt das Layout keine Rolle - dieser Code hat in jedem Falle wohldefiniertes Verhalten. Wir können gerne diskutieren, dass die Compilerunterstützung hier möglicherweise nicht immer genügt. Aber dann sollte deine Argumentation auch darauf abzielen: dass dieses Sprachfeatures zu komplex wäre, um mit vertretbarem Aufwand korrekt implementiert zu werden - ich meine aber aber, das die Verbreitung dieses Features eher gegen diese These spricht.

    Falls du meinst, so etwas tue man doch nicht: Erstens wird fast alles, was der Compiler erlaubt, auch getan, und zweitens tut Microsoft das höchstselbst in den MFC.

    Ja, ich bestreite nicht, dass in der Praxis viele seltsame Dinge gemacht werden. Ich sehe trotzdem nicht, wie das deine Position hinsichtlich des diskutierten Sprachfeatures unterstützt.



  • Shade Of Mine ich muss dir aber wiedersprechen. Wo hast du in Java viele Vererbungen? Ich habe hier ein 10.000 Line Projekt, und fast gar keine Vererbungen. Wieso erbst du von JFrame?

    class MeinFenster extends JFrame
    

    besser ist doch:

    class MeinFenster { private JFrame frame; }
    

    Ich wuerde von JFrame nur dann erben, wenn ich die protected-Methoden von JFrame brauche. Also wenn ich das Verhalten eines JFrame aendern, erweitern moechte. Kurz: Wenn ich mein eigenes veraenderes JFrame brauche. Aber sowas Spezielles brauchst du doch nie.

    Dann default-Implementierung von Interfaces, wozu? Was bitte soll ein Seriazable default machen? Oder ein Cloneable? Oder ein MouseListener?

    In der SDK hast du abstract class MouseAdapter implements MouseListener, MouseMover, MouseWheel aber das nur um Tipparbeit zu sparen. In MouseAdapter machen alle "geerbten" Methoden nichts, aber du brauchst nicht alle Methoden von z.B. MouseListener hinzuschreiben, wenn du nur mouseClick() brauchst.

    In Java hast du viele Objekte, ich benuzte sehr viele annonyme Klassen. Meine Hauptklassen erben meisten nichts und implementieren nichts, damit ich die Vererbungshirarhie so einfach wie moeglich habe.



  • DEvent schrieb:

    default-Implementierung von Interfaces, wozu? Was bitte soll ein Seriazable default machen? Oder ein Cloneable? Oder ein MouseListener?

    es gibt durchaus konzepte, bei denen eine default implementierung sinn macht. ich benutz bei manchen methoden z.b. gern interfaces als callbacks. error handler u.ä. und für diese biete ich dann default implementierungen an, die irgendwas "sinnvolles" machen. im falle eines error handlers z.b. das schreiben von informationen in eine log datei.

    das anbieten einer default implementierung hat dabei drei gründe:
    1. die notwendigkeit einer konkreteren implementierung ist selten aber vorhanden
    2. die API soll nicht mit unnötig vielen methoden zugekleistert werden
    3. die default implementierung ist ein praktisches beispiel



  • Shade Of Mine schrieb:

    Kapiere ich nicht.
    Warum ist es besser von IFooable zu erben und den Code von AbstractFoo::doFoo() zu copy&pasten als direkt von AbstractFoo zu erben? Welchen Vorteil bringt mir das?

    Du musst keinen Code Copy-Pasten. Ich erklärs nochmal: Du definierst ein Interface IFooable und schreibst die Standard-Implementierung DefaultFooable implements IFooable. Nun möchtest Du Fooables schreiben, die diese Standardimplementierung nutzen. Nun leitest Du aber nicht direkt mit extends von DefaultFooable ab sondern nutzt DefaultFooable in diesen Klassen per Komposition. Zusätzlich implementieren diese Klassen aber auch IFooable und delegieren in den entsprechenden Methoden an DefaultFooable.

    Warum das besser ist also direkt mit extends davon zu erben, kannst Du in diversen OO-Büchern lesen. Du hast bei direkter Vererbung von Implementierungscode im Gegensatz zur reinen Schnittstellenvererbung eine viel stärkere Kopplung zwischen den Klassen. Eine Klasse, die ein anderes Objekt per Komposition benutzt ist nicht so stark abhängig von diesem Objekt wie eine Klasse, die von einer anderen Code erbt. Schonmal versucht, eine Klassenhierarchie zu warten, die sich über 10+ Hierarchiestufen erstreckt?

    Sobald Du von einer konkreten Klasse erbst, bist Du für immer und ewig an die konkrete Implementierung der Oberklasse gebunden. Nutzt Du hingegen Komposition, kannst Du die Implementierung problemlos auch zu einem späteren Zeitpunkt noch austauschen.

    Bei der reinen Schnittstellenvererbung kann hingegen wenig schief gehn. Es ist schade, dass Du Dich mit dem Interface-Konzept nicht anfreunden kannst. Das Konzept ist nämlich insofern genial weil es die Möglichkeit mit sich bringt, Spezifikationen zu schreiben fernab von Implementierungsdetails. In der Java-Welt gibts haufenweise Sun-Spezifikationen, die alleine auf Interfaces basieren (z.B. JPA, JMX, ...).
    Wie macht Ihr das im Team, wenn z.B. Schicht 1 etwas von Schicht 2 benötigt? Man definiert ein Interface. Das ist in kürzester Zeit geschrieben und man kann loslegen.



  • byto schrieb:

    Du musst keinen Code Copy-Pasten. Ich erklärs nochmal: Du definierst ein Interface IFooable und schreibst die Standard-Implementierung DefaultFooable implements IFooable. Nun möchtest Du Fooables schreiben, die diese Standardimplementierung nutzen. Nun leitest Du aber nicht direkt mit extends von DefaultFooable ab sondern nutzt DefaultFooable in diesen Klassen per Komposition. Zusätzlich implementieren diese Klassen aber auch IFooable und delegieren in den entsprechenden Methoden an DefaultFooable.

    OMG, dann habe ich einen sinnlosen Member der von einer reinen logischen Sicht keinen Sinn macht.

    vector hat ein serializable.

    Klingt komisch. Ist komisch.

    Warum das besser ist also direkt mit extends davon zu erben, kannst Du in diversen OO-Büchern lesen. Du hast bei direkter Vererbung von Implementierungscode im Gegensatz zur reinen Schnittstellenvererbung eine viel stärkere Kopplung zwischen den Klassen. Eine Klasse, die ein anderes Objekt per Komposition benutzt ist nicht so stark abhängig von diesem Objekt wie eine Klasse, die von einer anderen Code erbt. Schonmal versucht, eine Klassenhierarchie zu warten, die sich über 10+ Hierarchiestufen erstreckt?

    Danke aber genau hier unterscheiden sich unsere Vorstellungen.
    Für mich ist ein Interface nicht durch Java definiert.

    Erkläre mir mal genau warum es so toll ist member einzubauen die meistens komplett nutzlos sind, anstatt direkt polymorphie zu verwenden. Warum muss ich ein DefaultFoo mitschleppen wenn ich das garnicht brauche?

    mal abgesehen davon, dass ein DefaultFoo nur sehr eingeschränkten nutzen hat, da er ja keinen Zugriff auf meinen State bekommt.

    Das lustigste Beispiel ist hier wohl das Java Serializeable. Das ist ein interface dass implementierungen anbietet. Nur dass man statt foo.serialize() lieber serialize(foo) schreibt um eben das große Probleme zu umgehen dass man keinen Code in interfaces haben kann.

    Aber ob ich foo(serialize) oder serialize(foo) schreibe ist komplett egal von einem OO standpunkt aus.

    Sobald Du von einer konkreten Klasse erbst, bist Du für immer und ewig an die konkrete Implementierung der Oberklasse gebunden. Nutzt Du hingegen Komposition, kannst Du die Implementierung problemlos auch zu einem späteren Zeitpunkt noch austauschen.

    Das ist falsch. Wozu habe ich polymorphie?

    Bei der reinen Schnittstellenvererbung kann hingegen wenig schief gehn. Es ist schade, dass Du Dich mit dem Interface-Konzept nicht anfreunden kannst.

    Es kann das selbe schief gehen was bei vererbung einer konkreten Klasse passieren kann. Ich bin nämlich fix an das Interface gebunden und daher an die semantik. Selbes bei einer vererbung einer konkreten Klasse. Die implementierung auszutauschen ist _nie_ ein problem - die schwierigkeit ist eine semantik auszutauschen.

    Ich kann mich übrigens sehr wohl mit Interfaces anfreunden - nur habe ich ein Problem mit der Java Definition von Interfaces und finde die C++ Definition einfach besser. Deshalb rede ich von Konzepten damit ihr nicht automatisch die Java Definition annehmt.

    Das Konzept ist nämlich insofern genial weil es die Möglichkeit mit sich bringt, Spezifikationen zu schreiben fernab von Implementierungsdetails. In der Java-Welt gibts haufenweise Sun-Spezifikationen, die alleine auf Interfaces basieren (z.B. JPA, JMX, ...).
    Wie macht Ihr das im Team, wenn z.B. Schicht 1 etwas von Schicht 2 benötigt? Man definiert ein Interface. Das ist in kürzester Zeit geschrieben und man kann loslegen.

    Und wo beisst sich das mit meiner Definition?
    Nirgendwo.

    Ich kann Schnittstellen implementieren die keinen Code enthalten - das ist absolut kein Problem. Die schwierigkeit tut sich erst auf, wenn ich schnittstellen habe die Code enthalten. Man kann den Java serializable weg gehen, aber dann verliert man polymorphie oder man geht den copy&paste weg der einfach nur bescheuert ist oder man definiert member die komplett sinnlos sind.

    Wenn man Interfaces nicht so restriktiv behandelt, habe ich _alle_ Vorteile von Java Interfaces und _alle_ Vorteile von c++ Klassen. Der Nachteil ist lediglich etwas selbstdisziplin. Aber dass das augenauswischerei ist, dass zu erkennen dazu muss man sich nur einmal mittelmäßigen java code ansehen wo 100.000 interfaces implementiert werden.

    Schlechten Code kann ich immer schreiben und für guten Code muss ich mich immer konzentrieren...



  • Shade Of Mine schrieb:

    OMG, dann habe ich einen sinnlosen Member der von einer reinen logischen Sicht keinen Sinn macht.

    Wenn Du nicht verstehen kannst, warum es eben häufig sinnvoller ist, an einen bei Bedarf austauschbaren Member zu delegieren, statt eine konkrete Klasse zu erweitern, dann brauchen wir an dieser Stelle eigentlich gar nicht weiter diskutieren.
    Es gibt genug Literatur, die sich damit beschäftigt. Vielleicht solltest du die dann erstmal lesen.



  • Shade Of Mine schrieb:

    Erkläre mir mal genau warum es so toll ist member einzubauen die meistens komplett nutzlos sind, anstatt direkt polymorphie zu verwenden. Warum muss ich ein DefaultFoo mitschleppen wenn ich das garnicht brauche?

    mal abgesehen davon, dass ein DefaultFoo nur sehr eingeschränkten nutzen hat, da er ja keinen Zugriff auf meinen State bekommt.

    Wie erhaelt den die Basisklasse einen Zugriff auf den State der Unterklasse?

    class Foo extends DefaultFoo { Bar state; }
    
    class Foo implements Fooable { private DefaultFoo deffoo; Bar state; }
    

    In beiden Faellen kann doch DefaultFoo nicht auf den State von Foo zugreifen?





  • DEvent: die default-implementation als basisklasse kann von der abgeleiteten klasse überschriebene methoden aus der endgültigen implementierung verwenden. das kompositum nicht.

    /edit: http://www.berniecode.com/writing/inheritance/ - genau von da geht shade of mine doch aus - mehrfach erben von konkreten klassen.



  • Ich möchte Mehrfachvererbung zumindestens in C++ nicht missen. Nicht wegen den klassischen Pro/Contras der Umsetzung wegen, sondern auf Grund der Templateprogrammierung um Klassen konfigurierbar zu machen (Wird beispielsweise in der Loki-Bibliothek intensiv verwendet).

    Ganz davon abgesehen halte ich Mehrfachvererbung unter Umständen durchaus für sinnvoll, wobei ich es dennoch nur sehr selten und nur unter Abwägung der Konsequenzen ensetzen würde.

    cu André



  • DEvent schrieb:

    Wie erhaelt den die Basisklasse einen Zugriff auf den State der Unterklasse?

    Reflection und Method Calls. Ich kann ja einfach eine Methode aufrufen die ich in der Basisklasse als virtuell (uU pure virtual) definiert habe. Und Reflection ist auch oft ein heisser Tip.

    byto schrieb:

    Wenn Du nicht verstehen kannst, warum es eben häufig sinnvoller ist, an einen bei Bedarf austauschbaren Member zu delegieren, statt eine konkrete Klasse zu erweitern, dann brauchen wir an dieser Stelle eigentlich gar nicht weiter diskutieren.
    Es gibt genug Literatur, die sich damit beschäftigt. Vielleicht solltest du die dann erstmal lesen.

    Erklär mir mal warum. Nicht aus technischer Sicht, sondern aus logischer sicht.

    Wie kann ich in UML erklären, dass ein vector ein DefaultSerializeable hat. Vorallem: vector ist ein serializable (oder implementiert serializable schnittsetlle) UND hat ein DefaultSerializable, dass vector aber garnicht verwendet, weil das ein überbleibsel aus der Vererbung von Container ist.

    So ein Design ist ausserhalb der Java-Interfaces Welt einfach nur Schrott. Und ich würde es nichtmal in Java umsetzen. Vorallem da es ja nichtmal immer technisch möglich ist - und logisch ist es sowieso nicht.

    Aber bitte, erkläre mir ganz einfach warum ISerializable keine Defaultimplementierung anbieten darf. In Java definiert Serializable hardcoded was es macht - ich kann mich nicht hineinhängen wenn ich will (zB wenn ich meine Klasse verschlüsseln will oder ähnliches). Warum ist es untragbar dass ich serializable customizen will? Java geht zB den Ansatz über Reflection um statische Member der Klasse auszulesen um ein customizing zu ermöglichen. Ist das wirklich der beste Ansatz und polymorphie hat hier garnichts verloren?



  • @Shade Of Mine - Serializable ist ein Marker-Interface und hat sonst nicht viel mit den konventionellen Interfaces zu tun. Das ist ein Spezialfall, den Java unter der Haube benutzt. Ob das schön umgesetzt ist oder nicht, ist eine Frage für sich und hat nichts mit dem Vererbungs- und Interface-Konzept ansich zu tun. Also keine Ahnung, warum Du dieses Thema hier immer wieder auspackst!?

    Es geht mir im übrigen auch überhaupt nicht um Java. Das von mir angesprochene ist sprachunabhängig und betrifft alle OO-Sprachen. In Java gibts halt ein extra Sprachmittel, um Schnittstellen zu definieren. In C++ erreicht man den gleichen Effekt wohl durch abstrakte Typen, die nur abstrakte Methoden haben und keine Implementierung beinhalten. Das war aber überhaupt nicht der Punkt.
    Der Punkt ist, dass Vererbung von konkreten Code (egal ob Mehrfach oder Einfach) häufig zu extrem schlecht wartbarem Code führt und keine lose Kopplung fördert, wie es bei Komposition der Fall ist.



  • byto schrieb:

    @Shade Of Mine - Serializable ist ein Marker-Interface und hat sonst nicht viel mit den konventionellen Interfaces zu tun. Das ist ein Spezialfall, den Java unter der Haube benutzt. Ob das schön umgesetzt ist oder nicht, ist eine Frage für sich und hat nichts mit dem Vererbungs- und Interface-Konzept ansich zu tun. Also keine Ahnung, warum Du dieses Thema hier immer wieder auspackst!?

    Eben weil es ein wunderschönes Beispiel dafür ist, dass die Java Interfaces nicht jedes Problem lösen können das ohne der Interface Restriktion trivial wäre.

    In C++ erreicht man den gleichen Effekt wohl durch abstrakte Typen, die nur abstrakte Methoden haben und keine Implementierung beinhalten. Das war aber überhaupt nicht der Punkt.

    Der Punkt ist, dass viele Leute, vorallem wenn man Java programmiert hat, das Konzept von Interfaces komplett falsch verstehen.
    Deshalb darf man nicht in Interface und Klasse trennen - zumindest nicht so wie es Java macht.



  • Shade Of Mine schrieb:

    Der Punkt ist, dass viele Leute, vorallem wenn man Java programmiert hat, das Konzept von Interfaces komplett falsch verstehen.
    Deshalb darf man nicht in Interface und Klasse trennen - zumindest nicht so wie es Java macht.

    Verstehe nicht, was Du meinst. Was versteht wer Deiner Meinung nach falsch und wie? 🤡
    In meinen Augen macht Java es genau richtig: Java-Interfaces erlauben keine Implementierung, also werden die Entwickler in die richtige Richtung gedrückt, Schnittstellen-Spezifikation und Implementierung voneinander zu trennen.



  • byto wech schrieb:

    Shade Of Mine schrieb:

    Der Punkt ist, dass viele Leute, vorallem wenn man Java programmiert hat, das Konzept von Interfaces komplett falsch verstehen.
    Deshalb darf man nicht in Interface und Klasse trennen - zumindest nicht so wie es Java macht.

    Verstehe nicht, was Du meinst. Was versteht wer Deiner Meinung nach falsch und wie? 🤡
    In meinen Augen macht Java es genau richtig: Java-Interfaces erlauben keine Implementierung, also werden die Entwickler in die richtige Richtung gedrückt, Schnittstellen-Spezifikation und Implementierung voneinander zu trennen.

    Und ich warte immer noch auf eine logische Begründung warum vector ein DefaultSerializable besitzt welches ein ISerializable ist und vector selbst ist ebenfalls ein Serializable. Warum ist diese is-a + has-a beziehung soviel besser als eine reine is-a beziehung?

    ich will nichts von implementierungsdetails wissen - ich will eine logische begründung haben wie du ein serializable implementieren willst. In Java ist das nicht ohne hacks möglich. Und sobald man hacken muss ist das design imho fehlerhaft. Und man kann nichtmal alles hacken - ich kann zB meine serialisierten daten nicht verschlüsseln oder mich anders serialisieren als java es will.

    Und genau das ist der Punkt: java muss hier so häßlich sein weil die trennung interface/klasse falsch ist.


Anmelden zum Antworten