Richtiger OO-Design-ansatz für "Parallel-Vererbung" ??



  • Servus,

    ich bin gerade dabei eine Library zu refaktoren und möchte einen Teil, den ich in ein anderes Projekt linke extrem verschlanken. Leider steh' ich mir mit meiner Vererbungshirarchie etwas im Weg, aber ich möchte Codeverdoppelungen um jeden Preis vermeiden.

    Also im Augenblick sieht es so aus:

    B ist die Basisklasse die enorm viel Funtkionalität bereitstellt.
    Davon abgeleitet Klasse C mit nur wenigen Verfeinerungen
    Davon (also von C, nicht von 😎 abgeleitet Klasse C mit kleinen Verfeinerungen
    Und weitere Ableitungen D von C geeerbt, E von D geerbt. Wohlgemerkt alles public-Vererbung.

    In dem einen Projekt, dass gegen meine Lib linkt, wird die gesamte Funktionalität (die in Basisiklasse B steht) auch alle gebraucht.
    In dem anderen möchte ich aber nur gegen eine verschlankte Version von B linken, brauche aber die Ableitungen C,D,E und F (abgeleitet von der verschlankten Version).

    Der intuitive Ansatz wäre jetzt natürlich eine verschlankte Klasse
    B_slim zu schreiben, die einerseits Basisklasse vom "dicken" B ist, aber andererseits auch Basisklasse von C_slim, die widerum von D_slim usw...
    Nun sind aber die Verfeinerungen C_slim und D_slim 1:1 identisch mit C und D. Und auf Code Copy&Paste hab ich keine Lust.

    Das muss doch eleganter gehen!

    Vorschläge?



  • Vielleicht kann man ja mittels projektbezogenen Defines einzelne Funktionen der Klasse ein/ausbauen.



  • Oh gott,
    bitte nein. Alles nur das nicht. Ich bin schon so froh, dass ich das ganze Makro-Gedöns aus dem Quellcode raushabe, jetzt fang ich nicht an, den Präprozessor doch wieder zu benutzen.
    Ifdefs arten grundsätzlich in Orgien aus, wo hinterher keiner mehr weiß, was eigentlich kompiliert wird.

    Ich suche, wenn möglich, ne reine OO-Lösung, aber trotzdem danke für den Vorschlag.



  • PhilippM schrieb:

    Das muss doch eleganter gehen!

    Ja. In dem man Vererbung kritisch hinterfragt. Vererbung als solches ist an sich kein schlechtes Sprachmittel, aber es wird viel zu intensiv verwendet.

    Wobei hier zwischen Schnittstellenvererbung (Interfaces/Virtuelle Basisklassen) und Implementierungsvererbung zu unterscheiden ist. Ersteres ist meist unproblematischer als letzeres.

    Grundsätzlich kann man Komplexität auch durch Umwandlung von Vererbungshierachien durch Aggregationen (Sprich: Statt x ist ein y, x hat ein y) lösen, was meist sogar die Bessere Wahl ist. Letzteres macht das Design auch häufig flexibler, da man die Implementierung quasi zur Laufzeit austauschen kann (Nehmen wir man an z1 und z2 seien beides Klassen die ein Interface y bereitstellen. Mit "x hat ein y" kann man hier eine lose Kopplung erzeugen).

    cu André



  • Grundsätzlich stimme ich dem absolut zu. Oftmals wird Vererbung nur genutzt, weil es in etwa funktioniert. Aber in meinem Problem ist es wirklich eine sehr sinnvolle Vererbungshierarchie, weil sie einen realen Sachverhalt sehr gut abbildet und mit wenig Bruch aus der Analyse-Phase direkt in die Implementierung übernommen werden kann und sehr gut funktioniert. Daher sehe ich da keine nicht-sinnentstellende Möglichkeit, auf die Vererbung zu verzichten.



  • PhilippM schrieb:

    Daher sehe ich da keine nicht-sinnentstellende Möglichkeit, auf die Vererbung zu verzichten.

    Dann musst du auch mit den Konsequenzen leben.

    Ganz davon abgesehen ist folgendes immer ein Designfehler:

    PhilippM schrieb:

    B ist die Basisklasse die enorm viel Funtkionalität bereitstellt.

    Eine Klasse sollte immer nur einen Aspekt abdecken, zu viel Verantwortung ist nicht nur in der Programmierung meist schwer verdaulich...

    cu André



  • Lass C doch von einem Templateparameter erben.



  • Das klingt gut, werde mal drüber nachdenken. Allerdings muss ich dann an extrem vielen Stellen umbauen (um überall <> zu machen).

    @asc:

    Eine Klasse sollte immer nur einen Aspekt abdecken, zu viel Verantwortung ist nicht nur in der Programmierung meist schwer verdaulich...

    Auch da stimme ich dir absolut zu!
    Ich hab mich da schlecht ausgedrückt, die Basisklasse bietet eigentlich nicht viel Funktionalität in dem Sinne, das viel arbeit abgefackelt wird, sonder eher dass es enorm viele Methoden des Zugriffs auf Instanzvariablen gibt, von denen ich aber 80% im Anwendungsfall 2 nicht brauche.



  • PhilippM schrieb:

    Ich hab mich da schlecht ausgedrückt, die Basisklasse bietet eigentlich nicht viel Funktionalität in dem Sinne, das viel arbeit abgefackelt wird, sonder eher dass es enorm viele Methoden des Zugriffs auf Instanzvariablen gibt, von denen ich aber 80% im Anwendungsfall 2 nicht brauche.

    Auch das deutet auf einen Designfehler.

    Klassen die sehr viele Methoden und/oder Membervariablen haben, sind:
    a) Schwer zu warten
    b) Häufig unnötig "mächtig" (Darauf deutet auch das du 80% im zweiten Anwendungsfall nicht brauchst)

    In Code Complete [Microsoft Press] wird sogar ganz drastisch empfohlen das Klassen nur 3-7 (ich hoffe mich richtig zu erinnern) Membervariablen haben sollten. Auch wenn das in vielen Fällen als unnötig angesehen wird, habe ich inzwischen selbst die Erfahrung gemacht das es leichter ist wenn man sich daran hält. Lieber schauen ob sich bestimmte Teile nicht in kleinere, dafür leicht zu wartende, Einheiten zerlegen lässt. Diese können dann wiederum Member in der vorherigen Klasse darstellen.

    cu André



  • Noch etwas anderes. Wenn du je nach Anwendungsfall wirklich nur einen kleinen Auszug brauchst, wäre dies sogar in einer Vererbungslastigen Hierachie eher ein Zeichen dafür, das Aufgaben aus der Klasse "weiter unten" in der Hierachie stehen müssten.

    cu André



  • Sollte es im 21 Jahrhundert nicht die Aufgabe des Compilers geworden sein, doppelten Code zu entfernen?

    Es gibt doch Einstellungen um unreferenzierten Code nicht zu erzeugen.



  • Listing schrieb:

    Sollte es im 21 Jahrhundert nicht die Aufgabe des Compilers geworden sein, doppelten Code zu entfernen?

    Es gibt doch Einstellungen um unreferenzierten Code nicht zu erzeugen.

    second that

    statisches linken vorausgesetzt.



  • Das hatte ich auch gehofft. Aber ich hab den Test gemacht und einfach mal brutal eine ganz neue Klasse geschrieben unter Inkaufnahme von Codeverdoppelung und habe einen Größenunterschied von 30% ausgemacht. Das ist bei einer Anwendung die Echtzeitkritisch ist und später in einer embedded-Umgebung landet echt nicht zu verachten.


Log in to reply