Als Java-Entwickler noch C/C++ dazulernen: Chancenlos?



  • Abgesehen davon passt es hinten und vorne nicht.
    Fangen wir mal an,

    Dokument erbt von Filename
    OpenFileDialog erbt auch von Filename
    BitmapDocument erbt von Document und Bitmap
    Application erbt von Hauptmenu und Dokument und Hauptfenster
    Das Hauptfenster erbt von Window, OKButton, CancelButton, Rahmen, ClientArea

    Schon kann eine Application nur ein Dokument haben.
    Das muss natürlich weggetricksat werden, schlage template<size_t number,typename T> TReingeerbtesAttribut vor, dann kann man mehrere gleichartige reingeerbte Attribute wenigstens anhand einer Nummer ausenanderhalten. char wäre noch besser, weil viel lesbarere und Variablennamen mit mehr als einem Buchstaben hat man noch nie gebraucht.

    Die Application hat auch einen Dateinamen, also erbst sie direkt einen Dateinamen und indirekt noch einen über das Dokument.

    Da muß man doch merken, daß der Ansatz kaputt ist.

    Und das macht muir Angst, was ist, wenn man dabei tatsächlich mal Vererbung ausdrücken möchte? Die geht in dem ganzen Müll ja total unter.



  • Ohne hier mitdiskutieren zu wollen, da das eh sinnlos ist, nur ein kleiner Hinweis:

    mixins

    Der erste Schritt ist natuerlich Mixins mit Mehrfachvererbung zu implementieren, weil es simpel wirkt. Das hat sich aber als nicht so toll rausgestellt und deshalb macht man es lieber so: http://www.drdobbs.com/cpp/mixin-based-programming-in-c/184404445 (mit Templates)

    Das ganze ist auch schon wieder 12 Jahre her.



  • knivil schrieb:

    Was du machst ist keine Objektorientierung im Sinne des Erfinders.

    Das habe zum einen auch nie behauptet und zweitens kannst Du die Aussage genauso wenig belegen, wie ich die (nie getroffene) Aussage, dass es im Sinne des Erfinders ist.
    Dafür müssten wir erstmal rausfinden, wer das Design Pattern der objektorientierten Programmierung erfunden hat. Mich interessiert auch nicht die Intention irgendeines Erfinders, der sitzt schließlich nicht vor meinem Quelltext.
    Interessanterweise hat der Erfinder aber auch private und protected-Ableitungen in C++ vorgesehen. Die Privaten Ableitungen nutze ich nicht, weil ich dann auch klar rausstellen möchte, dass eine Variable abgeschirmt ist.
    Da Du den Erfinder von OOP so gut kennst, würde mich mal dessen Intention dazu interessieren.

    OOP löst ein Problem. Ich löse Probleme. Und um Probleme zu lösen, kann man sich einschränken lassen oder nicht. Und wenn ich Dinge anders modellieren möchte als Du oder Volkard das für richtig halten, so ist das nicht gleichbedeutend mit 'falsch', es entspricht lediglich nicht dem Lehrbuch.
    Es hat sich aber für mich als praktisch erwiesen. Das ist erwähnenswert und genausowenig falsch, wie OOP zu Zeiten, als OOP noch ein synonym für Unsinn war.
    Ich will kein Lehrbuch schreiben, ich will Probleme lösen.

    Liskovsches Substitutionsprinzip... sicher, dass Du das als Argument gegen Mehrfachvererbung anbringen möchtest?

    Lieber Knivel, wie ich Deinen Wunsch nach Lehrbuch-Programmierung respektiere und dagegen nicht argumentiere, weil ich genauso gut weiß, dass das auch funktioniert, wäre es durchaus nicht zuviel verlangt, wenn Du akzeptierst, dass das Lehrbuch nicht für alle Menschen eine Bibel darstellt und andere Menschen zum Teil andere Wege gehen und damit gut klarkommen.

    volkard schrieb:

    Abgesehen davon passt es hinten und vorne nicht.
    Fangen wir mal an,

    Dokument erbt von Filename
    OpenFileDialog erbt auch von Filename
    BitmapDocument erbt von Document und Bitmap
    Application erbt von Hauptmenu und Dokument und Hauptfenster
    Das Hauptfenster erbt von Window, OKButton, CancelButton, Rahmen, ClientArea

    Schon kann eine Application nur ein Dokument haben.

    Dein Posting zeigt zwei Dinge: Erstens, dass Du Dir Dinge zusammengefasst hast, die hier nicht geschrieben wurden, die in Deinen Augen nicht passen. Was hat das mit mir zu tun!? Woher kommt eigentlich OpenFileDialog? Warum sollte Application von Document erben? Das würde nur Sinn ergeben, wenn die Applikation selbst als Dokument aufträte, es also eine Applikation für Applikationen gabe.

    Zweitens: In dem Du Dinge falsch zusammenbaust, baust Du Dir kein Argument auf. Du kannst Application von einer Liste von Dokumenten erben lassen, schon ist das semantisch vollkommen in Ordnung.
    Dir ist vor allem ein wichtiger Punkt entgangen, den ich schon beschrieb: Wenn Du eine Architektur über Mehrfachvererbung aufbaust, musst Du deutlich präziser typisieren. Das hast du bei den Buttons schon gemacht: Sie sind unterscheidbar.

    Ich versuche es erneut und ich arbeite mit Beispielen(!). Beispiel heißt einfaches und für ein Posting kleines Anschauungsobjekt, nicht zwangsläufig der perfekte Fall, aber geeignet um eine Idee zu transportieren, wenn man sich für die Idee interessiert und statt sofort versucht, alles abzustreiten erst die Möglichkeit akzeptiert, dass es eine Idee sein könnte.

    Nehmen wir eine Linie. Eine Linie kann man modellieren als ein Objekt, das zwei Punkte hat. Wir definieren also Punkt und Linie, packen zwei Punkte in die Klasse Linie, die wir mit Startpunkt und Endpunkt ansprechen und fertig.

    Mache ich das über Mehrfachvererbung, so muss ich mehr definieren: Ich kann ja nicht zweimal von Punkt ableiten. Ich muss also Typen definieren, die ich von Punkt ableite, aber unterscheidbar sind: Startpunkt und Endpunkt. Herzlich Willkommen im Diamanten. Es ist aber nicht das Diamant-Problem, es ist das Diamant-Feature. Diamant-Feature? Schonmal gehört? Steht in keinem Lehrbuch drin, weil der Diamant ist was böses in der Programmierung. So etwas darf man nicht als Feature beschreiben. Warum darf ich die logischen Eigenschaften denn eigentlich nicht nutzen? Weil in einem Lehrbuch steht, dass der Diamant Böse ist? Ist hier schon einer vom Wichsen blind geworden, oder was?

    Also Diamant-Feature.

    Welchen Sinn macht das Ganze? Ich muss nun "umständlicher" auf die Koordinaten zugreifen:

    line.Startpunkt::x
    
    statt
    
    line.Startpunkt.x
    

    Ist jetzt nicht der riesen Unterschied, oder? Es passiert intern auch nichts anderes, es ist ja auch nichts anderes: Es ist eine Aggregation, die über Mehrfachvererbung modelliert wurde.
    Ich wurde lediglich genötigt, den Membernamen über Typnamen zu modellieren.

    Ich kann nun aber Funktionen schreiben, die sich ausschließlich mit dem Startpunkt ODER mit dem Endpunkt beschäftigen und das auch in der Signatur zum Ausdruck bringen. Ich muss jetzt auch nicht aufpassen, dass ich die beiden Punkte versehentlich vertausche, denn sie haben unterscheidbare Datentypen. Die Information, ob ich mit einem Startpunkt oder Endpunkt arbeite, kann ich viel länger erhalten und bei einer Funktion, die einen Startpunkt verlangt, muss ich nicht darauf hoffen, dass der Entwickler auch wirklich line.Startpunkt reinwirft, er wirft einfach das komplette Objekt rein und der Compiler greift das richtige Objekt raus. Als Entwickler bin ich überhaupt nicht mehr in der Lage, den falschen Punkt in die Funktion zu werfen (allerdings müssen Konstruktoren ggfs. explicit sein).

    Ich sehe das als Vorteil. Das ist von C++ nicht unbedingt optimal unterstützt, es ist schließlich kein Weg aus dem Lehrbuch. Oder umgekehrt: Vielleicht ist das Lehrbuch auch einfach der üblichen Benutzung von Strukturen in der Historie der Programmiersprachen angelehnt. Wäre auch eine Möglichkeit. Wurde das erste Lehrbuch zur OOP eigentlich geschrieben, bevor der erste OOP gemacht hat oder wurde erst gegen gängige Programmierrichtlinien verstoßen und dann ein Buch geschrieben!?
    Ein Lehrbuch ist ein Aggregation von Papier, seine Existenz ist kein Beweis, dass da der Weisheit letzter Schluss drinsteht - oder überhaupt irgendetwas sinnvolles. Man darf Lehrbücher und Lehrer in Frage stellen.

    Aber was ich tue ist modellierbar und man darf sich fragen, was diese Form der Modellierung ändert und ob das in manchen Situationen Vorteile bringt. Es funktioniert. Oder man klammert sich mit Händen und Füßen daran, dass eine andere Perspektive nicht sein darf, wie Knivils es zeigt, weil man ausschließlich von seinem Standpunkt aus auf Fahrräder gucken darf und er glaubt, weil er ein paar mathematische Symbole um seine Worte packt, dass das bedeutet, dass eine andere Perspektive nicht in mathematische Symbole zu kleiden wäre.

    ∀(Zweirad) : Zweirad ∈ { FahrzeugMitVorderrad } ∧ Zweirad ∈ { FahrzeugMitHinterrad }

    Sowas ist doch keine konstruktive Diskussion!? Das ist Schwanzvergleich. Schäufelchenklauen im Sandkasten. Kurz: Kinderkacke. Langweilig.

    Ich finde diese Form der Modellierung in vielen Fällen praktisch, ausbaufähig und sehe Potential darin. Und ich werde mich weiter damit beschäftigen. Fertig.

    Ich habe mich nie gegen die Programmierung mit Membern ausgesprochen oder gar geschrieben, dass ich zum Programmieren nichts anderes mehr tue als ausschließlich abzuleiten.
    Aber ich nutze auch diese Möglichkeit meine Datenstrukturen zu modellieren und wenn ihr Argumente habt, die dagegen sprechen, dann bin ich interessiert die zu hören. Knivils "Das ist falsch" ohne weiteren Kommentar oder sich Ableitungshierarchien auszudenken und darauf basierend Schlussfolgerungen zu ziehen, dass es bei mir vorne und hinten nicht passt, ist keine Argumentation.
    Und eine Meinungsverschiedenheit ist akzeptabel, wenn beide Seiten die Toleranz besitzen, das hinzunehmen.
    Vielleicht verabschiede ich mich irgendwann von meiner heutigen Ansicht, vielleicht ihr. Das kann man doch so stehen lassen, wenn man sich nicht einig wird, oder?



  • Wo ist bei eine ungerichtet Linie der Startpunkt und wo der Endpunkt?



  • 😮



  • Mich interessiert auch nicht die Intention irgendeines Erfinders

    Und den Matheteil hast du ueberlesen?

    private und protected-Ableitungen in C++ vorgesehen

    Ergaenzungen, sie sind fuer das Grundkonzept nicht noetig.

    so ist das nicht gleichbedeutend mit 'falsch'

    Ja, du hast den Matheteil uberlesen.

    als OOP noch ein synonym für Unsinn war

    Nenne mir den Zeitpunkt, wann das so war.

    Liskovsches Substitutionsprinzip... sicher, dass Du das als Argument gegen Mehrfachvererbung anbringen möchtest?

    Du hast nicht verstanden. Substitutionsprinzip beschreibt die Mathematik. So wie du Mehrfachvererbung benutzt entspricht es nicht den Regeln der Mathematik. Au weia Dogma ... boese.

    für alle Menschen eine Bibel darstellt

    Ich halte Vernunft bei vernuenftigen Menschen fuer zwingend. Du stellst es als Religion dar und willst mich in die Fanatikerschiene druecken.

    andere Menschen zum Teil andere Wege gehen und damit gut klarkommen

    Klar kommt man ohne Vernunft klar, wenn man allein und abgeschieden lebt. Dort wird kein Mathe gebraucht wird. Aber Programmierer sollte man trotzdem nicht werden.

    Deine Argumentation baut darauf auf, dass nur Lehrbuchmeinung wiedergekaeut wird. Dem ist nicht so. Einfach mal die Orginale lesen und versuchen zu verstehen. Als ob du der einzige selbstdenkende hier bist.

    Was denkst du denn. Wenn heute jemand noch mit dem Bohrschen Atommodel die Physik betreibt so wird er hoechstens muede belaechelt. Klar kan er viele Ergebnisse produzieren, aber sie beruhen auf einem falschen Model. Niemand macht sich die Muehe zuzuhoeren oder gar die Veroeffentlichungen zu lesen. Sie werden schlicht ignoriert.



  • Es gibt scheinbar schon wirklich Gründe warum MI problematisch ist, z.B. das hier: http://stackoverflow.com/questions/225929/what-is-the-exact-problem-with-multiple-inheritance Es gibt als Alternative zu MI auch die Traits in Scala. Die sind allerdings auch nicht unproblematisch, da sie stateful sind. Damit spielt Reihenfolge der Initialisierung ebenfalls eine Rolle wie bei MI, siehe http://blog.jetbrains.com/kotlin/2011/08/multiple-inheritance-part-2-possible-directions/ Ich finde die Mixins ind D sind eine sehr schöne Lösung: http://dlang.org/template-mixin.html Nur schade, dass D so wenig Wiederhall findet.

    Okay, zurück zum eigentlichen Thema. Ich bin nämlich der, der den Thread eröffnet hat... Aber lasst euch von eurer Diskussion über "to MI or not to MI" nicht stören ;-).

    Also ich habe im Studium mal 2 Semesterarbeiten mit C++ gemacht und auch meine Diplomarbeit. Ich bin also so auf fortgeschrittenem Anfängerniveau bis unterem mittlerem Niveau. Ich denke das Problem dürfte eher plain C sein. C-Code kann manchmal ziemlich verworren sein. Wir haben eine ältere C-Anwendung bei uns im Geschäft in Produktion. Da werde ich mir den Code holen und den versuchen zu verstehen. Das dürfte mir auf die Sprünge helfen den normal üblen C-Code zu verstehen. Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung. Auch wenn RAII vieles einfacher macht, ist es doch nicht alles und muss auch erst verstanden werden ...

    Also dann schau ich mal. Wollte schon immer mal C/C++ machen. Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))). Dachte aber dass es mittlerweile chancenlos ist. Aber es gibt doch nicht so wenige Stellenbeschreibungen für Java-Entwickler, in denen steht, das Kenntnisse in C++ ein Plus sind. Dann wünscht mir Glück :-).

    Cheers, Saxo



  • Saxo schrieb:

    Ich denke das Problem dürfte eher plain C sein.

    Jo. Inzwischen sind die Sprachen eben viel weiter.

    Saxo schrieb:

    Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung.

    Nö. Verwende einfach kein new/delete, ohne vorher hier zu fragen.

    Saxo schrieb:

    Wollte schon immer mal C/C++ machen.

    Das ist wirklich noch schlechter als C. Finger weg von C/C++-Büchern.

    Saxo schrieb:

    Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))).

    Nö. Man muß bei weitem nicht alle C++-Tricks können, um sehr gute Programme schreiben zu könnne. Hier führt man gelegentlich vor, wie man mit fünf Schachfiguren jongliert, einen Schachfigur übers Haus wirft, aus den Buchstaben S,C,H,A,C,H,F,I,G,U,R das Wort GRAFISCH bauen kann, und in der Tat, wer so begeistert dabei ist, dass er echt gar nix anderes mehr im Kopf hat, ist bei sonst vergleichbaren Umständen oft auch der bessere Schachspieler.



  • Saxo schrieb:

    Es gibt scheinbar schon wirklich Gründe warum MI problematisch ist

    Die Frage ist halt, ob man versteht, was man da beschreibt oder nicht.
    Problematisch ist entsprechend, wenn man mit Mehrfachvererbung Dinge macht, die man nicht (richtig) versteht.

    Saxo schrieb:

    Okay, zurück zum eigentlichen Thema. Ich bin nämlich der, der den Thread eröffnet hat... Aber lasst euch von eurer Diskussion über "to MI or not to MI" nicht stören ;-).

    Die Diskussion hängt sich an mir auf, weniger an der Mehrfachvererbung. Das ganze funktioniert nicht, wenn ich nicht dagegen halte.

    Saxo schrieb:

    Also ich habe im Studium mal 2 Semesterarbeiten mit C++ gemacht und auch meine Diplomarbeit. Ich bin also so auf fortgeschrittenem Anfängerniveau bis unterem mittlerem Niveau. Ich denke das Problem dürfte eher plain C sein. C-Code kann manchmal ziemlich verworren sein. Wir haben eine ältere C-Anwendung bei uns im Geschäft in Produktion. Da werde ich mir den Code holen und den versuchen zu verstehen. Das dürfte mir auf die Sprünge helfen den normal üblen C-Code zu verstehen. Was C++ selbst angeht, denke ich, liegt die Hauptschwierigkeit in der Speicherverwaltung. Auch wenn RAII vieles einfacher macht, ist es doch nicht alles und muss auch erst verstanden werden ...

    Lass Dir von volkard keine Mist erzählen. RAII ist eine wunderbare Technik, die Dir hilft Probleme zu vermeiden. Und ich stimme volkard absolut zu, dass RAII (fast?) immer die richtige Antwort ist, wenn man qualitative C++-Standard-Anwendungen schreiben möchte. Manchmal will man aber eben keine Standard-Anwendungen schreiben und dann ist es auch nicht verkehrt, wenn man bei (void 😉 nicht gleich Spastiken entwickelt.

    RAII ist aber weder - wie er andeutet - die einzige Antwort, noch musst Du Dir irgendwo Erlaubnis holen, wenn Du new oder delete verwendest. Ich denke, die volkards Aufforderung hier sagt genug über seine Position aus, um diese angemessen einschätzen zu können.

    Saxo schrieb:

    Also dann schau ich mal. Wollte schon immer mal C/C++ machen. Man muss einfach seine eigene virtuelle Maschine in C/C++ schreiben können :-))). Dachte aber dass es mittlerweile chancenlos ist. Aber es gibt doch nicht so wenige Stellenbeschreibungen für Java-Entwickler, in denen steht, das Kenntnisse in C++ ein Plus sind. Dann wünscht mir Glück :-).

    Es ist die Frage, wieviel Zeit Du damit verbringen willst und kannst. Geh es locker an, spiele damit. Programmieren kann Spaß machen, ein Job sollte Spaß machen und es ist zur Weiterbildung durchaus nützlich selbst Erfahrungen zu sammeln, wo z.B. RAII hilfreich ist, statt RAII einfach anzuwenden, ohne zu wissen weshalb. Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.
    Dinge (meistens) richtig zu machen, ohne zu begreifen was man da tut, bedeutet nicht, dass man C++ programmieren kann.

    Und wenn Du Spaß an einer VM hast... ganz ehrlich... eine kleine VM ist vergleichsweise schnell geschrieben. Aufwendiger finde ich die Daten für die VM zu produzieren. Wenn das ein Ziel ist, an dem Du Spaß hast, Du kannst viel damit spielen, experimentieren und somit auch viel über C++ lernen.

    Viel Spaß 🙂



  • Xin schrieb:

    Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.

    🙄 Abgesehen davon würde ich mir wünschen du könntest es auch so darstellen.



  • Zeus schrieb:

    Xin schrieb:

    Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.

    🙄 Abgesehen davon würde ich mir wünschen du könntest es auch so darstellen.

    Du kannst die Augen verdrehen, wie Du magst, aber ich habe aber kein Problem mit dem Diamanten. Ich kann den Diamanten nicht als Problem darstellen, wenn ich ihn auch nutzen kann. Der Diamant ist - wie jede Programmiertechnik - etwas was Vor- und Nachteile besitzt und damit muss man sich auseinander setzen.
    Genauso wie Listen Vor- und Nachteile gegenüber Arrays haben und man eben nicht uneingeschränkt das eine empfehlen kann, weil man die Nachteil des anderen nicht mag.

    Mehrfachvererbung kann durchaus so verwendet werden, dass überhaupt keine Diamant-Konstruktion entsteht. Also wenn der Diamant für Dich ein Problem darstellt, so gibt es immernoch keinen Grund, sich gegen die Mehrfachvererbung auszusprechen. Sprich Dich dann lieber dafür aus, bei Mehrfachvererbung auf gemeinsame Basisklassen zu verzichten, um die versehentliche Konstruktion eines Diamanten zu verhindern.

    Einige User in diesem Forum sind verhältnismäßig enttäuschend hier unterwegs: Als ob der eigene Wunsch maßgeblich für den Rest der Welt wäre oder volkards Aufforderung hier nachzufragen, ob man new oder delete verwenden darf. Was ist das hier - Inquisition?
    Akzeptiert bitte auch, dass andere Entwickler andere Erfahrungen machen und jeder das Recht hat, seine Erfahrungen zu machen und daraus die Schlüsse zu ziehen, die jedem erlauben, seine Probleme so qualitativ wie möglich zu arbeiten.
    Ein Forum sollte dazu dienen, über diese Schlüsse zu beraten, Hilfestellung oder Inspiration zu geben, sich auszutauschen.

    Aber weder hat hier einer das Recht, Erklärungen einzufordern, noch Vorgaben zu einer Problemlösung machen, schonmal gar nicht, wenn das Problem überhaupt nicht bekannt ist oder gar sich bzw. das Forum als die entscheidende Instanz für anderer Leute Programmierdesign darzustellen. Genausowenig besteht das Recht, erst die Augen zu verdrehen und dazu Wünsche in dieser Form zu äußern.

    Darüber bitte mal ganz unabhängig von C++ mal nachdenken.



  • Xin schrieb:

    marco.b schrieb:

    Xin schrieb:

    ich spielte durchaus schon mit dem Gedanken, Aggregationen komplett abzuschaffen: weniger Boilerplate und zwangsläufig scharfe Typdefinitionen. Das will man doch!?

    Wenn ich richtig verstanden habe, wie du Vererbung einsetzt, koppelst du aber auch stärker. Stichwort als Beispiel: Inversion of Control.

    Wieso kopple ich stärker?

    Nehmen wir an, du hast eine abstrakte Basisklasse "Log", welche lediglich rein virtuelle Methoden besitzt, etwa log(int code, string message).
    Nun gibt es die Subtypen
    SystemEventLog : Log
    FileLog : Log
    TcpLog : Log
    welche alle ein Anwendungsprotokoll hin zu verschiedenen Datenquellen implementieren.

    Wenn du nun keine Aggregation/Komposition sondern nur Vererbung verwendest und du in Klasse Worker ein Protokoll benötigst, hast du zwei Möglichkeiten:

    a)

    class Worker : Log { ... }
    

    Wäre rein logisch offenbar falsch. Schließlich willst du kein Log implementieren, sondern eins verwenden. Ferner kannst du obige Implementierungen nicht wiederverwenden.

    b)

    class Worker: FileLog
    

    Also das Erben von einer konkreten, implementierenden Klasse. Damit hast du aber eine fest Abhängigkeit gekoppelt.
    Mit Aggregation könntest du dagegen einen Member vom Typ Log deklarieren, die konkrete Implementierung aber durch Polymorphie-Zauber dynamisch auflösen, etwa weil du Protokollkanäle dynamisch konfigurierbar halten willst. In Java/C# werden solche Aufgaben üblicherweise mit Dependency Injection Frameworks gelöst. Das klappt aber bei deiner Vererberei nicht.



  • Moin marco!

    Danke für den erklärenden, nicht abfälligen Tonfall. So macht der Austausch jedenfalls Spaß.

    marco.b schrieb:

    Mit Aggregation könntest du dagegen einen Member vom Typ Log deklarieren, die konkrete Implementierung aber durch Polymorphie-Zauber dynamisch auflösen, etwa weil du Protokollkanäle dynamisch konfigurierbar halten willst. In Java/C# werden solche Aufgaben üblicherweise mit Dependency Injection Frameworks gelöst. Das klappt aber bei deiner Vererberei nicht.

    Eine schöne Problemstellung, für die ich sogar bevorzugt Vererbung wählen würde!

    Ich kann in C++ keinen Member vom Typ Log deklarieren, da Log ja eine abstrakte Basisklasse ist.

    Das geht in Java, aber da wird ein "Referenztyp" auch als "Log " definiert. Entsprechend muss ich ebenfalls einen (Log) nehmen, bzw. wenn ich das ausschließlich zur Initialisierungszeit für immer festlegen möchte, dann habe ich in C++ auch die Möglichkeite ein (Log &) zu nehmen. Damit wäre die Kopplung frei bis zur Initialisierung. Das würde aber nicht der Java-Referenz entsprechen und warum sollte ich mich festlegen, Logger niemals wechseln zu können!?

    Möchte ich das Problem wie von Dir beschrieben lösen, würde ich einen LogForwarder definieren, den ich nicht von Log abgeleiten würde, aber inline das gleiche Interface besitzt und der einen Member vom Typ (Log 😉 besitzt. Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende! Ich verwende sie vor allem da, wo ich sie verstecken will. Hier muss ich einen Member nehmen, weil ich in C++ ja gar nicht von (Log 😉 ableiten kann. Ich kann diese Funktionalität in C++ also anders gar nicht formulieren.

    Das ist - wie ich schon sagte - mehr Arbeit, um die Typen zu definieren, hat aber den Vorteil (und deswegen würde ich es so implementieren), dass ich die Frage, ob ich überhaupt Loggen kann (Member ist nullptr) einmalig im LogForwarder klären kann und nicht in jeder Klasse erst den Member fragen muss, ob er Null ist. Ich logge einfach, wenn kein Logger da ist, muss die Frage, ob wirklich geloggt wird, in LogForwarder geklärt. Ich bin also im Gegensatz zu einem Member nicht in Gefahr, mal die Frage zu vergessen und ein SegFault zu provozieren und falls ich doch auf einem SegFault stoße, dann weiß ich, dass das Problem ausschließlich in der Klasse LogForwarder zu suchen ist und ich garantieren kann, dass dieser Bug Problem danach für das komplette Programm erledigt ist.

    Du wolltest ein Log als Member nehmen. Du schreibst

    if( logmember ) logmember->Log( "Bla" );
    

    ich schreibe

    Log( "Bla" );
    

    Beides funktioniert gleichwertig. Bei mir wird die Frage inline dazugepackt, Du musst sie schreiben. Ich kann garantieren, dass ich niemals auf logmember zugreife, wenn es null ist, Du musst sehr diszipliniert sein, um das niemals zu vergessen. Ich bin diszipliniert, mag mich aber nicht aus dem Fenster lehnen, bei so etwas eine Garantie auszusprechen, also typisiere ich eigentlich so ziemlich alles.

    Nehme ich einen Member, bin ich verlockt, einfach ein Log * zu nehmen - es ist einfach weniger Arbeit. Um das Problem über Vererbung zu lösen MUSS ich mein Problem genau zu typisieren.
    Um ebenfalls eine Garantie aussprechen zu können, müsstest auch Du eine Klasse LogForwarder als Member nehmen. Du hast also die gleiche Arbeit, die Forwarderklasse zu schreiben und schreibst dann

    logmember.Log( "Bla" );
    

    Alle Lösungen lösen das gleiche Problem. Nur Du schreibst mehr als ich und wer mehr schreibt, macht mehr Fehler.



  • Xin schrieb:

    Ich kann in C++ keinen Member vom Typ Log deklarieren, da Log ja eine abstrakte Basisklasse ist.

    Natürlich. Ich meinte Log*, tut mir leid ich habe eine erschreckend hohe C#-Affinität, die sich gern mal ungewollt zeigt.

    Xin schrieb:

    Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende!

    Auf den Fall bezog sich aber meine Argumentation. Du schriebst ja auf Seite 2, dir schon überlegt zu haben, auf Aggregation komplett zu verzichten. Das hast du ja relativiert.

    Ich logge einfach, wenn kein Logger da ist, muss die Frage, ob wirklich geloggt wird, in LogForwarder geklärt.

    Das ist natürlich ein berechtigter Einwand. In C# (jaja ich weiß...) würde ich das mit Dependency Injection und zusätzlich Contract Based Design lösen: Das Vorhandensein des Logs ist eine Invariante des Typs, respektive eine Vorbedingung der jeweiligen Methode.

    In C++ oder alternativ, je nach Requirement, würde ich den Logger hingegen einmalig im Konstruktor auf nullptr prüfen und ihn dann ggf. auf eine Dummy-Instanz [nenne ich ganz gerne Blackhole Log] setzen, die die Log-Methoden implementiert und schlicht nichts tut. Das geht in Richtung des Null Object Patterns.



  • marco.b schrieb:

    Xin schrieb:

    Ich kann in C++ keinen Member vom Typ Log deklarieren, da Log ja eine abstrakte Basisklasse ist.

    Natürlich. Ich meinte Log*, tut mir leid ich habe eine erschreckend hohe C#-Affinität, die sich gern mal ungewollt zeigt.

    Kein Problem, es war ja abzusehen, was gemeint war.

    marco.b schrieb:

    Xin schrieb:

    Nochmal: Dass ich mehr Ableitungen verwende, bedeutet nicht, dass ausschließlich ableite und ich keine Member verwende!

    Auf den Fall bezog sich aber meine Argumentation. Du schriebst ja auf Seite 2, dir schon überlegt zu haben, auf Aggregation komplett zu verzichten. Das hast du ja relativiert.

    C++ fehlt hier die Möglichkeit, dies auszuformulieren. Darum kann ich in C++ leider nicht auf Member verzichten.

    Wie gesagt, ich arbeite an einer eigenen Sprache. Leider bin ich noch nicht ganz soweit, dass ich ernsthaft vererben kann, aber ich werde da durchaus mehr Experimente machen können als in C++ und syntaktisch meine Art der Programmierung deutlich stärker unterstützen, so dass das ganze hoffentlich auch offensichtlicher ist und sich hoffentlich auch die Konsequenzen herausarbeiten, die in C++-Quelltext nicht vollständig darstellbar sind.

    Ziel ist hierbei durchaus aus Member komplett verzichten zu können.

    marco.b schrieb:

    In C++ würde ich den Logger hingegen einmalig im Konstruktor auf nullptr prüfen und ihn dann ggf. auf eine Dummy-Instanz [nenne ich ganz gerne Blackhole Log] setzen, die die Log-Methoden implementiert und schlicht nichts tut. Das geht in Richtung des Null Object Patterns.

    Ebenfalls eine vom Design her absolut saubere Lösung, die - falls häufiger geloggt als verworfen wird - auch die bessere ist - schließlich würde dann häufig eine überflüssige Frage gestellt. Werden die Logeinträge häufiger verworfen als geloggt, würde ich meine Variante als die schnellere bevorzugen.

    Auch hier sieht man wieder, dass es eben nicht darum geht, eine Lösung als die Perfekte zu verkaufen, es sind zwei Lösungen, die Vor- und Nachteile mit sich bringen und man eben von Fall zu Fall entscheiden muss, was angebrachter ist.



  • Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...



  • dot schrieb:

    Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...

    TypeStates! 😋



  • dot schrieb:

    Mich würde interessieren, wie genau du vor hast, in so einem System, das auf Member komplett verzichten soll, Zustand zu repräsentieren...

    Ich erfinde nicht die Programmierung neu.

    Es sieht nicht nach C++ aus, aber man kann den Aufbau schon ähnlich wie in einer C++-Klasse nachvollziehen.

    Ich würde es eher als Perspektivenwechsel beschreiben oder als Reduktion aufs Wesentliche - inhaltlich ändert sich dabei genauso wenig, wie bei der Frage, ob man ableitet oder einen Member nimmt. Doch je weniger Fragen ich stelle, desto weniger Fragen muss der Entwickler beim Aufbau der Klasse beantworten.
    Entsprechend wird die Definition von Datenstrukturen vergleichsweise sehr kurz.

    Wenn's fertig ist und funktioniert, werde ich es vorstellen, aber das wird noch dauern, schließlich bezahlt mich für die Sprache keiner.



  • Xin schrieb:

    Zeus schrieb:

    Xin schrieb:

    Genauso mit der Mehrfachvererbung, wo alle nur vom das Diamant-Problem warnen und nur wenige die Logik der Mehrfachvererbung einfach annehmen und für ihre Zwecke nutzen. Dafür muss man sich damit aber halt auseinander setzen und das geht nicht, wenn man damit keine Erfahrungen sammeln darf.

    🙄 Abgesehen davon würde ich mir wünschen du könntest es auch so darstellen.

    Du kannst die Augen verdrehen, wie Du magst, aber ich habe aber kein Problem mit dem Diamanten. Ich kann den Diamanten nicht als Problem darstellen, wenn ich ihn auch nutzen kann. Der Diamant ist - wie jede Programmiertechnik - etwas was Vor- und Nachteile besitzt und damit muss man sich auseinander setzen.

    Lieber Xin,

    der Smily, der Kursiv- und der Unterstrichenen-Text sind nicht umsonst da, vielleicht hätte ich um es besser hervorzuheben etwas durchstreichen sollen, damit die Meldung besser rüber kommt. Ein Begriff und eine Konsequenz wurden markiert, der Kontext, den umgebenden Text, allerdings nicht. Ich weiß echt nicht, welchen übel Gesindel dir über den Weg gelaufen ist: der Anti-Java-Hype-, die Anti-MI-Einstellung *urgh*. Ob du, ich oder jemand anders ein Problem mit dem Diamant-Problem hat, ist bedeutungslos. Von Bedeutung ist die Anwendung der Mehrfachvererbung und die Verantwortung über das Diamant-Problem herr zu werden. Zur Verdeutlichung finde ich daher schon angemessen von ein Problem zu reden.

    Xin schrieb:

    Akzeptiert bitte auch, dass andere Entwickler andere Erfahrungen machen und jeder das Recht hat, seine Erfahrungen zu machen und daraus die Schlüsse zu ziehen, die jedem erlauben, seine Probleme so qualitativ wie möglich zu arbeiten.

    Häh? Kann nicht an mich gerichtet sein, muss eine Massage an alle sein, oder so.

    Xin schrieb:

    Ein Forum sollte dazu dienen, über diese Schlüsse zu beraten, Hilfestellung oder Inspiration zu geben, sich auszutauschen.

    Du blockst doch aktive. Meine Frage ist noch nicht beantwortet. Aber eigentlich muss du es auch nicht beantworten,..

    Xin schrieb:

    Aber weder hat hier einer das Recht, Erklärungen einzufordern, noch Vorgaben zu einer Problemlösung machen, schonmal gar nicht, wenn das Problem überhaupt nicht bekannt ist oder gar sich bzw. das Forum als die entscheidende Instanz für anderer Leute Programmierdesign darzustellen.

    Häh? Kann nicht an mich gerichtet sein, muss eine Massage an alle sein, oder so.

    Xin schrieb:

    Genausowenig besteht das Recht, erst die Augen zu verdrehen und dazu Wünsche in dieser Form zu äußern.

    Wie bitte oben hattest du mir die Freiheit gegeben und jetzt willst du mein Recht nehmen. Du weiß schon Meinungsfreiheit 😉



  • Xin schrieb:

    Das ist - wie ich schon sagte - mehr Arbeit, um die Typen zu definieren, hat aber den Vorteil (und deswegen würde ich es so implementieren), dass ich die Frage, ob ich überhaupt Loggen kann (Member ist nullptr) einmalig im LogForwarder klären kann und nicht in jeder Klasse erst den Member fragen muss, ob er Null ist. Ich logge einfach, wenn kein Logger da ist, muss die Frage, ob wirklich geloggt wird, in LogForwarder geklärt. Ich bin also im Gegensatz zu einem Member nicht in Gefahr, mal die Frage zu vergessen und ein SegFault zu provozieren und falls ich doch auf einem SegFault stoße, dann weiß ich, dass das Problem ausschließlich in der Klasse LogForwarder zu suchen ist und ich garantieren kann, dass dieser Bug Problem danach für das komplette Programm erledigt ist.

    Das Null Object Pattern gibt es schon ewig. Das ist kein bisschen neu oder einzigartig.


Anmelden zum Antworten