設計:親クラスへのコールバック


13

子を持つオブジェクトをモデリングするとき、親クラスのメンバーとして、構成を介して子を含めるのが一般的です。ただし、子が親に何かを伝える必要がある場合もありますが、親の関数を呼び出す必要があります。C ++を使用してこれをどのように達成できますか?いくつかのオプションは次のとおりです。

  1. 親クラスをグローバルにします。したがって、子オブジェクトは親オブジェクトのメンバー関数を呼び出すことができます。

  2. すべての子オブジェクトに、親オブジェクトをポインターまたは参照として注入します。子が親オブジェクトに何かを伝える必要がある場合、使用できるメンバー変数があるため、子は常にそうすることができます。

これを行う他の方法は何ですか?この種の一般的なデザインパターンや名前はありますか?

他のオブジェクト指向言語では詳細が異なるため、C ++のアイデアとソリューションに興味があることに注意してください。たとえば、上記のポイント2では「ポインターまたは参照」に言及しており、両方ともC ++でのみ可能です。C ++には他の言語にはない言語機能があるため、問題の解決策を実装すると、これらの言語機能が組み込まれる可能性があり、他の言語で考えられるソリューションとは異なるソリューションになります。


例を追加できますか?これはあなたの質問の有効な例ですか?orderitems(子)を持つorderobject(= parent)があり、orderitem-quantityが変更されたときに注文の合計を更新しますか?または完全に異なる何かを考えていますか?
k3b

@ k3bええ、それは有効な例です。子の一部の情報が変更され、親が何かをする必要があります。
sashang

各子クラスにいくつかのコンストラクタパラメータと参照データメンバーを追加するだけです。
tp1

子供は親についてすべてを知る必要がありますか、それとも簡単なdelegateもので十分ですか?
ジュリアンゲルトー

回答:


16

まず最初に、これはコードの匂いになります。親/子に構成を使用するポイントは、親が子について知っているが、その逆はないということです。特に、関係が「から構成される」というよりも「含む」という場合。

親への参照は可能であり、C ++ではかなり一般的です。他の言語では、関数オブジェクトまたはイベントがより頻繁に使用され、子供が部外者が知りたいことを伝えることができます。これは一般的なパブリッシュ/サブスクライブ型のパターンです。どちらがより慣用的かは、使用しているC ++のバージョンとコードベースの標準に依存していると思われます。


私の状況では、それはコード臭です。素晴らしい答え。
マーティンファイファー

3

他の人が指摘したように、親オブジェクトをポインタまたは参照として注入するという基本的な考え方は、原則として-行く方法です。

これには1つの欠点があります。親と子の間に循環依存関係が発生します。それを避けたい場合はIParent、親が継承する抽象基本クラス(インターフェース)を定義してください。IParent子が呼び出したい仮想関数としてメソッドを含める必要があります。次に、への参照として親を注入しIParentます。これにより、親オブジェクトをモックオブジェクトで簡単に置き換えることができるため、子の単体テストが非常に簡単になります。

子が親オブジェクトの1つの関数だけを呼び出す必要がある場合、完全なIParentクラスが大きすぎる可能性があります。この場合、メンバー関数へのポインターを子に挿入するか、そのメンバー関数をカプセル化するファンクターオブジェクトで十分です。


2

できることは、構成によって、子クラスの親への参照を保持することです。これにより、子は親を認識し、その上でパブリックメソッドを呼び出すことができます。

したがって、オプション2に進みます。ただし、親から子を削除する場合は、子の親への参照を削除し、null(または新しい親が1)。または、コンテキストに応じて、単に子オブジェクトを削除することもできます。


1

親への参照またはポインタを渡します。それらを親の友達にするか、呼び出されたメソッドをパブリックにすることができます。上記のいずれも実行したくない場合は、親のメソッドの1つをパブリックとして公開し、それ自体が親のネストされたクラスである「ブリッジ」オブジェクトを渡すことができます)。ただし、これは多くの状況で少し複雑すぎる場合があります。


1

2)ポインタまたは参照として親オブジェクトをすべての子オブジェクトに注入します。子が親オブジェクトに何かを伝える必要がある場合、使用できるメンバー変数があるため、子は常にそうすることができます。

完全に実行可能なオプションです。すべての現代言語には、別の言語を参照するために使用できる機能があります。


1

わずかに変化する同様のアプローチがありますが、利点があります。

親AにコンポーネントCが含まれているとします。

コンポーネントCで、InterfaceCを宣言し、それへの参照を保持します。これは、コンポーネントと外界とのインターフェースです。

親AはInterfaceCを実装し、その参照をコンポーネントCに設定します。コンポーネントCは、親AをInterfaceCとして認識します。

アイデアは次のとおりです。コンポーネントは、そのインターフェイスを使用して外部と通信します。

親を直接設定するよりもこれを使用する利点は次のとおりです。

コンポーネントが何かを行い、親に通知する必要があるとします。インターフェイスを呼び出します。後で、親を変更することにします。コンポーネントはまったく気にせず、変更を加えません。

後で、多くのオブジェクトにイベントを通知したいとします。InterfaceCのリストを作成し、それに参照を追加するだけです。

短所:親クラスは多くのインターフェイスを実装することになります(ただし、クラス宣言を見ると誰が話しているかがすぐにわかるので、これを利点と考えています)


0

これはc#固有であることに注意してください。c ++に類似したものがあるかどうかはわかりません。

プッシュボタン付きのguiフォームがある場合、通常はObserver_patternまたはPublish-subscribe patternとしても知られるEvent-Subscribtionを使用した別のアプローチがあります。

通常、プッシュボタンは、それが存在する特定のフォームを知りません。代わりに、ボタンがイベントをトリガーまたは発行し、フォームがサブスクライブされた通知を受け取り、それに応じて対応できます。

gui-sに加えて、このメカニズムはすべての親子関係で使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.