Favour Composition over Inheritance - Aber Wann?



  • Firefighter schrieb:

    mich wundert es halt das es dennoch so oft in vielen Clean-Code Buechern vorkommt

    Tja, die Clean-Code-Bewegung halte ich für eine Pest. 😉
    Sie ruft geradezu danach, mit religiösen Inhalten gefüllt zu werden.



  • otze schrieb:

    Firefighter schrieb:

    Gute Frage, aber sowas wuerde ich schon mit FCoI loesen.

    Andererseits kannst du dann deinen Ringpuffer nicht dort verwenden, wo du auch ein Array verwenden könntest. Wenn die Implementation aber so schön ist, dass du überall dort, wo du ein Array hast, auch einen Ringpuffer verwenden könntest, dann spricht das deutlich für Vererbung.

    Das hängt davon ab, wie das Array definiert ist. Weder C-Arrays noch std::array oder std::vector sind geeignet, davon abzuleiten, geschweige denn dass sie ein (runtime-)polymorphes Interface anbieten. Von diesen Arrays würde ich also keinen Rungpuffer ableiten.



  • volkard schrieb:

    Firefighter schrieb:

    mich wundert es halt das es dennoch so oft in vielen Clean-Code Buechern vorkommt

    Tja, die Clean-Code-Bewegung halte ich für eine Pest. 😉
    Sie ruft geradezu danach, mit religiösen Inhalten gefüllt zu werden.

    Ich wuerde jetzt nicht gleich "Pest" sagen.
    Einige Ansatzpunkte sind schon recht Wichtig die sie aufzaehlen, aber genau das FCoI ist eines wo ich mir nicht sicher bin ob das wirklich einen Mehr-Nutzen bringt.



  • Personen, Studenten, Beschaeftigte, Professoren

    Klingt klar nach Vererbung.
    Allerdings musst du aufpassen:

    class Hiwi : public Student, public Beschaeftigter
    {
      ...
    };
    

    und Student und Beschaeftigte erben jeweils von Personen -> Diamanten-Problem

    Für diesen Fall müsstest du dann evtl. über virtuelle Vererbung nachdenken.



  • Tachyon schrieb:

    Das ist wieder von der Sprache abhängig. In C++ würde ich das über freie Funktionen und Templates machen. Siehe boost.circular_buffer der sich wie alle anderen Container verhält.

    Und ob da sgeht, ist wieder Situationsabhängig:

    class MyClass{
    
    template<class Array>
    virtual void foo(Array const&);
    };
    

    oops.

    @pumuckl Das ist klar, dass das mit std::vector nicht (unbedingt) funktioniert. Aber guter Punkt.

    @Q Der Fall ist doch DAS Argument für FCoI.



  • otze schrieb:

    class MyClass{
    
    template<class Array>
    virtual void foo(Array const&);
    };
    

    oops.

    Häh 😕



  • virtuelle templates gehen nicht.



  • otze schrieb:

    virtuelle templates gehen nicht.

    Aber was hat das mit dem zu tun, was ich sagte?



  • Du hast einen Vorschlag ohne Vererbung gebracht mit "In C++ würde ich das über freie Funktionen und Templates machen.". Und ich zeige dir ein C++ Beispiel wo dieser Ansatz zu virtuellen templates führen muss: immer dann, wenn Polymorphie im Spiel ist. Das Problem hast du nicht, wenn du direkt ableitest, sofern es in dem Kontext möglich ist.

    Die Frage die FCoI impliziert: nämlich, "Wann braucht man Vererbung?" ist nicht pauschal lösbar. Man kann nicht sagen: "In Sprache X machst du das so". Bevor die Sprache ins Spiel kommt ist das eine Frage des Softwareentwurfs. Danach kommen erst die möglichen Implementationen ins Spiel.



  • otze schrieb:

    Du hast einen Vorschlag ohne Vererbung gebracht mit "In C++ würde ich das über freie Funktionen und Templates machen.". Und ich zeige dir ein C++ Beispiel wo dieser Ansatz zu virtuellen templates führen muss: immer dann, wenn Polymorphie im Spiel ist. Das Problem hast du nicht, wenn du direkt ableitest, sofern es in dem Kontext möglich ist.

    Die Frage die FCoI impliziert: nämlich, "Wann braucht man Vererbung?" ist nicht pauschal lösbar. Man kann nicht sagen: "In Sprache X machst du das so". Bevor die Sprache ins Spiel kommt ist das eine Frage des Softwareentwurfs. Danach kommen erst die möglichen Implementationen ins Spiel.

    a) Können freie Funktionen nicht virtuell sein, weshalb Dein Beispiel etwas am von mir Geschriebenen vorbei geht.
    b) Wollte ich auf die Standardalgorithmen und die Container hinweisen, wo Du Compilezeitpolymorphie hast. Aus sicht der Standardalgorithmen ist alles, was sich wie ein Randomaccess-Iterator verhält auch ein... nun ja Randomaccess-Iterator. Auch wenn Du nicht bla.puh() schreibst.



  • otze schrieb:

    Die Frage die FCoI impliziert: nämlich, "Wann braucht man Vererbung?" ist nicht pauschal lösbar. Man kann nicht sagen: "In Sprache X machst du das so". Bevor die Sprache ins Spiel kommt ist das eine Frage des Softwareentwurfs. Danach kommen erst die möglichen Implementationen ins Spiel.

    Danke, das lief mir grad naemlich wieder in die Falsche Richtung.



  • Tachyon schrieb:

    a) Können freie Funktionen nicht virtuell sein, weshalb Dein Beispiel etwas am von mir Geschriebenen vorbei geht.

    Dann geht deine erste Antwort aber an meinem Post vorbei:

    Wenn die Implementation aber so schön ist, dass du überall dort, wo du ein Array hast, auch einen Ringpuffer verwenden könntest, dann spricht das deutlich für Vererbung.

    //edit (heute editiere ich zu viel...)
    Mir geht es eigentlich nicht um den Gegensatz zwischen dynamischer<->statischer Polymorphie. Das ist für mich ein reines Implementationsdetail. Wenn der Softwareentwurf es zulässt, eine polymorphie zur Compilezeit aufzulösen, dann implementiert man das so. Wenn es nicht geht, dann geht es nicht. Die Sprachgegebenheiten sind da zweitrangig vor dem Entwurf.



  • otze schrieb:

    [...]ohne Vererbung gebracht [...] immer dann, wenn Polymorphie im Spiel ist.

    Merkst was? 😉

    Was Tachyon meinte ist eben, dass er eben NICHT den Ansatz über eine vererbte oder vererbbare Klasse gehen würde, sondern über templates. Ohne Vererbung und ohne dass Vererbung für irgendwann später vorgesehen wäre, und dementsprechend ohne dass es irgendwann zu virtuellen Methoden kommen müsste.

    C++ als Multi-Paradigmensprache hat genau da einige Fallstricke, im Vergleich zu "rein objektorientierten" Sprachen wie Java oder C#: in letzteren ist so ziemlich alles vererbbar und geerbt (mindestens von Object). In C++ heißt eine Klasse noch lange nicht, dass es was mit Vererbung auch nur zu tun hat. Es gibt genügend Klassen, die by design implizit nicht für Vererbung geeignet sind. Dann gibt es Klassen, die (private) Vererbung als eine Art der Kompositoin mit engerer Bindung benutzen, und Klassen, die die Mechanismen von Vererbung in Zusammenhang mit Templates benutzen, und es gibt Klassen, die tatsächlich als Teil einer echten objektorientierten Vererbungshierarchie anzusehen sind, wie man sie im Lehrbuch findet. Diese Vielfältigkeit macht es vor allem Ein- und Umsteigern in C++ nicht unbedingt einfach. Sie ist auch der Grund, warum das FCoI der CleanCoder die afaik häufiger aus dem "saubereren" Umfeld der Java/C#-Ecke kommen, im C++-Kontext nicht so blindlings angewandt werden kann.



  • pumuckl schrieb:

    otze schrieb:

    [...]ohne Vererbung gebracht [...] immer dann, wenn Polymorphie im Spiel ist.

    Merkst was? 😉

    Was Tachyon meinte ist eben, dass er eben NICHT den Ansatz über eine vererbte oder vererbbare Klasse gehen würde, sondern über templates. Ohne Vererbung und ohne dass Vererbung für irgendwann später vorgesehen wäre, und dementsprechend ohne dass es irgendwann zu virtuellen Methoden kommen müsste.

    C++ als Multi-Paradigmensprache hat genau da einige Fallstricke, im Vergleich zu "rein objektorientierten" Sprachen wie Java oder C#: in letzteren ist so ziemlich alles vererbbar und geerbt (mindestens von Object). In C++ heißt eine Klasse noch lange nicht, dass es was mit Vererbung auch nur zu tun hat. Es gibt genügend Klassen, die by design implizit nicht für Vererbung geeignet sind. Dann gibt es Klassen, die (private) Vererbung als eine Art der Kompositoin mit engerer Bindung benutzen, und Klassen, die die Mechanismen von Vererbung in Zusammenhang mit Templates benutzen, und es gibt Klassen, die tatsächlich als Teil einer echten objektorientierten Vererbungshierarchie anzusehen sind, wie man sie im Lehrbuch findet. Diese Vielfältigkeit macht es vor allem Ein- und Umsteigern in C++ nicht unbedingt einfach. Sie ist auch der Grund, warum das FCoI der CleanCoder die afaik häufiger aus dem "saubereren" Umfeld der Java/C#-Ecke kommen, im C++-Kontext nicht so blindlings angewandt werden kann.

    Ja das wird es wohl sein, das heisst wir koennen abschliessend sagen, das meine Frage im C++ Bereich nicht so einfach zu beantworten waere wie im Java/C# Bereich.

    Damit ist auch alles geklaert fuer mich 🙂



  • Firefighter schrieb:

    Ja das wird es wohl sein, das heisst wir koennen abschliessend sagen, das meine Frage im C++ Bereich nicht so einfach zu beantworten waere wie im Java/C# Bereich.

    Damit ist auch alles geklaert fuer mich 🙂

    So ein Unsinn, Objektorientiertung funktioniert in C++ genauso wie in C# oder Java.

    Du gibts zu wenig Informationen, um die Frage beantworten zu können. In welcher Beziehung stehen die Personen und vor allem: Was können sie tun (welche virtuellen Methoden gibt es)?



  • Firefighter schrieb:

    Aktuell sollen wir ein Projekt fuer die Uni machen wo wir ne einfache Personenhierachie einer Universitaet darstellen sollen (Personen, Studenten, Beschaeftigte, Professoren). Eigentlich ist das ein Paradebeispiel fuer Vererbung, jedoch wollte ich mich mal von der Vererbung loesen und Versuchen es mit Komposition zu loesen.

    Ich seh eine Klasse Person und drei Rollen (Studenten, Beschaeftigte, Professoren).



  • Zeus schrieb:

    Firefighter schrieb:

    Aktuell sollen wir ein Projekt fuer die Uni machen wo wir ne einfache Personenhierachie einer Universitaet darstellen sollen (Personen, Studenten, Beschaeftigte, Professoren). Eigentlich ist das ein Paradebeispiel fuer Vererbung, jedoch wollte ich mich mal von der Vererbung loesen und Versuchen es mit Komposition zu loesen.

    Ich seh eine Klasse Person und drei Rollen (Studenten, Beschaeftigte, Professoren).

    Hätte ich erst mal auch so gesagt. Bis jetzt wurde ja noch nicht mal angegeben, ob irgendeiner von denen was besonderes kann bzw. für was die überhaupt da sind.



  • Wuerde es mehr Informationen geben, haette ich die euch sicherlich gegeben oder?

    TyRoXx schrieb:

    Firefighter schrieb:

    Ja das wird es wohl sein, das heisst wir koennen abschliessend sagen, das meine Frage im C++ Bereich nicht so einfach zu beantworten waere wie im Java/C# Bereich.

    Damit ist auch alles geklaert fuer mich 🙂

    So ein Unsinn, Objektorientiertung funktioniert in C++ genauso wie in C# oder Java.

    Ich wollte auch gar nicht ausdruecken das es mit C++ so nicht geht, aber wir haben ja in der Diskussion davor festgestellt das es Sprachabhaengig ist und schon Unterschiede macht ob ich nun in C++ oder C#/Java unterwegs bin.



  • Ich hab mir die Aufgabe nochmal eben durchgelesen und sehe gerade das sich die Klassen eigentlich nicht durch Funktionalitaet unterscheiden sondern nur durch Attribute. Da bin ich ja der Meinung das mir Vererbung nicht wirklich was nutzt.
    Wenn ich keine Unterschieden im Verhalten habe sondern nur in Eigenschaften koennte man das ganze ja dann doch als Komposition aufziehen oder nicht? Ich meine Polymorphie bringt mir ja dann nicht mehr wirklich was wenn ich nen Zeiger auf ne Person irgendwo hin uebergebe und ich keine spezialisierten Funktionen habe, hab ich ja eh nur Information ueber die Personenattribute.

    Die Aufgaben unseres Dozenten sind nicht wirklich sinnvoll 🙄



  • Firefighter schrieb:

    Ich hab mir die Aufgabe nochmal eben durchgelesen und sehe gerade das sich die Klassen eigentlich nicht durch Funktionalitaet unterscheiden sondern nur durch Attribute. Da bin ich ja der Meinung das mir Vererbung nicht wirklich was nutzt.

    Ne, gerade dafür ist Polymorphie Da: Nach außen gleiches Verhalten, aber intern anderes Verhalten (oder andere Attribute).
    Dinge die von Person erben verhalten verhalten sich ja gerade auch wie Personen und nicht anders. Sonst wären sie gerade keine Personen.


Anmelden zum Antworten