Hallo zusammen,
ich habe ein Problem mit meinem, in Qt(C++) programmierten Programm:
Ich Versuche mein Problem zunächst relativ abstrakt zu erklären,
vielleicht kann mir dann schon jemand von euch den entscheidenden Tip
geben ohne einen riesen Roman lesen zu müssen. Es ist Komplex genug und
wird auch so schon viel zu lang werden...
Und zwar habe ich eine Mutter-klasse (Mother) welche die abstrakte
Kinder-klasse beaufsichtigen muss. Jedes Kind darf aber verschiedene
Funktionen der Mutter abrufen. Zum Beispiel dürfen alle Jungs eine
Funktion aufrufen (boysFunction()). Analog natürlich die Mädchen. Aber
es gibt viel viel mehr Funktionen einer Mutter (weiß ja jeder). Ich hab
nur exemplarisch mal zwei in mein Bild eingefügt. Im prinzip darf jedes
Kind jede der vielen Funktion bedienen, wird es aber nicht wollen.
Ein Lösung mit Qt und dem Signal-Slot-System wäre ja einfach...
Darauf möchte ich gar nicht weiter eingehen, das ist trivial.
Mein Problem ist, dass die Kinder dynamisch erzeugt werden. Die Mutter
erzeugt die Kinder mit einer Fabrik-Klasse, ich könnte sie jetzt
Gebärmutter nennen, nach Vorgaben in einem String (nennen wir ihn DNA).
Sie weiß also gar nicht ob sie einen Tom, eine Pia oder eine Lisa
produziert. Die Fabrik klasse soll aber kein riesen if-else-cluster
werden in dem je nach Kind die Signale und Slots verbunden werden.
Schöner wäre es dem Kind zu sagen, "das hier ist deine Mama" und das
kind weiß wo es die Funktionen der Mutter findet.
Leider ergibt sich dadurch aber ein kleiner header-deadlock, da die
Kinder wissen müssen wie die Mutter aussieht. Umgekehrt muss natürlich
die Mutter auch die (abstrakten) Kinder kennen. Und schon hab ich das
Henne-Ei Problem, naja der Compiler, ich nicht...
Kann mir jemand einen Tip geben wie man sowas geschickt lösen kann?
Oder war das jetzt zu abstrakt erklärt?
Viele Grüße
Luigi
Luigi schrieb:> da die> Kinder wissen müssen wie die Mutter aussieht. Umgekehrt muss natürlich> die Mutter auch die (abstrakten) Kinder kennen.
Außer forward abstraction auch immer hilfreich ist es, Abhängigkeiten in
einem Interface zu isolieren. In diesem Fall die Mutterfunktionen, die
von den Kindern aufgerufen werden können (IMother). Dann müssen die
Kinder nichts über die Mutter wissen, sie bekommen nur einen Pointer
o.ä. auf eine IMother.
Manchmal sind auch mehrere Interfaces sinnvoll, die die Mutter
implementiert. Die Mädchen wissen nur, dass die Mutter eine
IZöpfeflechterin ist, die Jungs kennen nur die IFußballaufpumperin. Die
Klischees bitte entschuldigen ;).
Die Mutter muss auch nichts über die Kinder wissen, sie kennt nur Child.
Den Unterschied zwischen Junge, Mädchen und Alien kennt nur der Uterus.
Das alles kann man gnadenlos übertreiben, bis hinter der Abstraktion
kein Programm mehr erkennbar ist, aber zum Verständnis sind übertriebene
Beispiele mit zuviel Architektur hilfreich.
IMother.h:
Noch etwas Ergänzung und Korrektur:
Wenn eine Klasse Foo nur einen Pointer/Referenz auf ein Blubb enthält,
reicht in Foo.h eine forward declaration "class Blubb;", und Blubb.h
muss nicht includet werden. Das ist immer sinnvoll und löst auch hier
das Problem der Kreis-Includes. In Foo.cpp muss Blubb.h includet werden,
um mit dem Pointer etwas anfangen zu können, aber das führt nicht zu dem
originalen Problem.
Damit bleiben beide Klassen eng gekoppelt und abhängig von der
Implementierung der jeweils anderen. Wenn das nicht stört, ist das die
einfachste und damit beste Lösung.
Ein anderer Ansatz ist das erwähnte Interface (forward-deklarieren kann
und soll man trotzdem). Manchmal ergibt sich das ganz natürlich aus der
Aufgabenstellung (anhand von weltfremden Beispielen kann man das nicht
entscheiden) und man schreibt in die Kinder keine unnötigen
Abhängigkeiten von unwichtigen Implementierungsdetails der Mutter
hinein.
Wenn man am Ende trotzdem alle Methoden der Mutter in das Interface
packt, hat man außer Komplexität nichts gewonnen; die späteren
Änderungen, die durch das Interface leichter würden, passieren sowieso
nie oder sind so anders als vorhergesehen, dass ein ganz anderer Ansatz
notwendig wird. YAGNI.
Das gilt für mein Riesen-Beispiel oben, IMother kann man ohne Probleme
rauswerfen, forward declaration ist völlig ausreichend. Wenn in Zukunft
verschiedene Mütterklassen auftauchen und eine Basisklasse/Interfacce
notwendig wird, kann man dies nachholen.
Die erwähnten mehrfachen Interfaces führen darüber hinaus zu viel Spaß
und Erkenntnisgewinn auf dem Gebiet der Mehrfachvererbung, wenn es
Überschneidungen zwischen den enthaltenen Methoden gibt.