Design: Rückruf an die übergeordnete Klasse

13

Wenn Sie ein Objekt mit untergeordneten Objekten modellieren, ist es üblich, die untergeordneten Objekte über die Komposition als Mitglied der übergeordneten Klasse einzuschließen. Manchmal müssen die Kinder dem Elternteil jedoch etwas mitteilen, sie müssen eine Funktion des Elternteils aufrufen. Wie kann dies mit C ++ erreicht werden? Einige Optionen sind:

  1. Machen Sie die übergeordnete Klasse global, damit die untergeordneten Objekte Elementfunktionen des übergeordneten Objekts aufrufen können.

  2. Injizieren Sie das übergeordnete Objekt als Zeiger oder Referenz in jedes untergeordnete Objekt. Wenn das Kind dann dem übergeordneten Objekt etwas mitteilen muss, kann es dies immer tun, da es eine Mitgliedsvariable hat, die es verwenden kann.

Was sind andere Methoden, um dies zu tun? Gibt es ein allgemeines Designmuster oder einen Namen für diese Art von Dingen?

Beachten Sie, dass ich an Ideen und Lösungen in C ++ interessiert bin, da die Details in anderen objektorientierten Sprachen unterschiedlich sein werden. Zum Beispiel erwähnt Punkt 2 oben 'Zeiger oder Referenzen' und beide sind nur in C ++ möglich. C ++ verfügt über Sprachfunktionen, die in anderen Sprachen nicht vorhanden sind. Daher werden bei der Implementierung einer Lösung für das Problem möglicherweise diese Sprachfunktionen integriert, wodurch sich die Lösung von denen unterscheidet, die jemand in einer anderen Sprache findet.

Sashang
quelle
Kannst du ein Beispiel hinzufügen? Ist dies ein gültiges Beispiel für Ihre Frage? Sie haben ein Bestellobjekt (= Eltern) mit Bestellpositionen (Kinder) und möchten die Bestellsumme aktualisieren, wenn sich eine Bestellpositionen-Menge ändert? oder denken sie an etwas ganz anderes?
k3b
@ k3b Ja, das ist ein gültiges Beispiel. Einige Informationen im Kind haben sich geändert, sodass die Eltern etwas unternehmen müssen.
Sashang
Fügen Sie jeder untergeordneten Klasse lediglich einige Konstruktorparameter und Referenzdatenelemente hinzu.
1.
Muss das Kind alles über die Eltern wissen, oder würde ein einfaches delegateausreichen?
Julien Guertault

Antworten:

16

Das erste, was zuerst kommt, kann ein Codegeruch sein. Der Sinn der Verwendung von Komposition für die Eltern / Kinder besteht darin, dass die Eltern die Kinder kennen, aber nicht umgekehrt. Vor allem, wenn die Relation mehr aus einem "enthält" als aus "besteht" besteht.

Ein Verweis auf das übergeordnete Element ist in C ++ möglich und weit verbreitet. In anderen Sprachen wird ein Funktionsobjekt oder -ereignis häufiger verwendet, um dem Kind die Möglichkeit zu geben, Dinge zu kommunizieren, die Außenstehende möglicherweise wissen möchten. Dies ist ein gängiges Publish-Subscriber-Muster. Ich vermute, dass das, was idiomatischer ist, von der verwendeten Version von C ++ und den Standards Ihrer Codebasis abhängt.

Telastyn
quelle
In meiner Situation ist es ein Codegeruch. Gute Antwort.
Martin Pfeffer
3

Wie bereits erwähnt, ist die Grundidee, das übergeordnete Objekt als Zeiger oder Referenz einzufügen, das Prinzip der Vorgehensweise.

Dies hat einen Nachteil: Sie erhalten eine zyklische Abhängigkeit zwischen dem Elternteil und dem Kind. Wenn Sie dies vermeiden möchten, definieren Sie eine abstrakte Basisklasse (eine Schnittstelle), IParentvon der Ihr Elternteil erbt. IParentsollte die Methoden als virtuelle Funktionen enthalten, die das Kind aufrufen möchte. Dann injizieren Sie den Elternteil als Verweis auf IParent. Dies erleichtert dem Kind das Unit-Testen erheblich, da Sie das übergeordnete Objekt jetzt problemlos durch ein Scheinobjekt ersetzen können.

Wenn Ihr Kind nur eine Funktion Ihres Elternobjekts aufrufen muss, kann eine komplette IParentKlasse übergroß sein. In diesem Fall reicht es aus, einen Zeiger auf die Elementfunktion in das untergeordnete Element oder ein Funktionsobjekt einzufügen, das diese Elementfunktion einkapselt.

Doc Brown
quelle
2

Sie können einen Verweis auf das übergeordnete Element in der untergeordneten Klasse nach Zusammensetzung beibehalten. Auf diese Weise kennt das Kind seine Eltern und kann öffentliche Methoden aufrufen.

Ich würde also mit Option 2 fortfahren. Sie müssen jedoch vorsichtig sein, wenn ein Kind vom Elternteil entfernt wird, müssen Sie den Verweis auf das Elternteil im Kind entfernen und auf null verweisen (oder auf ein neues Elternteil, falls dies der Fall ist einer). Oder Sie löschen einfach das untergeordnete Objekt, je nach Kontext.

Marco-Fiset
quelle
1

Übergeben Sie eine Referenz oder einen Zeiger an das übergeordnete Element. Sie können sie zu Freunden des übergeordneten Elements machen oder die aufgerufene Methode öffentlich machen. Wenn Sie keine der oben genannten Aktionen ausführen möchten, können Sie ihnen ein "Bridge" -Objekt übergeben, das eine der übergeordneten Methoden als öffentlich anzeigt und selbst eine geschachtelte Klasse des übergeordneten Objekts ist (daher hat es Zugriff auf jede übergeordnete Methode ). Dies kann jedoch in vielen Situationen etwas zu komplex sein.

quant_dev
quelle
1

2) Injizieren Sie das übergeordnete Objekt als Zeiger oder Referenz in jedes untergeordnete Objekt. Wenn das Kind dann dem übergeordneten Objekt etwas mitteilen muss, kann es dies immer tun, da es eine Mitgliedsvariable hat, die es verwenden kann.

Ist eine durchaus praktikable Option. Alle modernen Sprachen haben eine Funktion, mit der auf eine andere Sprache verwiesen werden kann.

DeadMG
quelle
1

Es gibt einen ähnlichen Ansatz mit geringfügigen Abweichungen, der jedoch Vorteile bietet:

Angenommen, Elternteil A enthält Komponente C.

Deklarieren Sie in Komponente C InterfaceC und verweisen Sie darauf. Dies ist die Schnittstelle der Komponente zur Außenwelt.

Übergeordnetes Element A implementiert InterfaceC und legt seine Referenz in Komponente C fest. Komponente C sieht übergeordnetes Element A als InterfaceC.

Die Idee ist: Eine Komponente spricht über ihre Schnittstelle nach außen.

Dies hat folgende Vorteile gegenüber der direkten Einstellung des übergeordneten Elements:

Angenommen, die Komponente führt etwas aus und muss das übergeordnete Element benachrichtigen. Es ruft die Schnittstelle auf. Später entscheiden Sie, ob Sie das übergeordnete Element ändern möchten. Die Komponente kümmert sich überhaupt nicht und Sie werden keine Änderungen daran vornehmen.

Sagen Sie später, Sie möchten viele Objekte über ein Ereignis benachrichtigen. Sie erstellen einfach eine Liste von InterfaceC und fügen Referenzen hinzu.

Nachteile: Eine übergeordnete Klasse implementiert am Ende viele Schnittstellen (ich halte dies jedoch für einen Vorteil, da ich anhand der Klassendeklaration sofort weiß, wer mit ihr spricht).

Makketronix
quelle
0

Beachten Sie, dass dies C # -spezifisch ist. Ich weiß nicht, ob C ++ etwas ähnliches hat.

Wenn Sie ein GUI-Formular mit Drucktasten haben, haben Sie normalerweise einen anderen Ansatz mit Event -Subskription, auch bekannt als Observer_pattern oder Publish-Subscribe-Muster .

Die Drucktaste kennt normalerweise nicht die spezifische Form, in der sie lebt. Stattdessen löst die Schaltfläche ein Ereignis aus oder veröffentlicht es , und das Formular erhält eine abonnierte Benachrichtigung und kann entsprechend reagieren.

Neben gui-s kann dieser Mechanismus in jeder Eltern-Kind-Beziehung verwendet werden

k3b
quelle