web-dev-qa-db-ja.com

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

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

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

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

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

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

13
sashang

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

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

16
Telastyn

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

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

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

3
Doc Brown

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

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

2
marco-fiset

わずかな変化を伴う同様のアプローチがありますが、これには利点があります。

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

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

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

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

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

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

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

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

1
Makketronix

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

1
quant_dev

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

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

1
DeadMG

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

押しボタン付きのGUIフォームを使用している場合は、通常、Event-Subscribtionまたは Observer_pattern を使用する別の方法があります。 発行–購読パターン

押しボタンは通常、それがどこにあるか特定していません。代わりに、ボタンがイベントをトリガーまたはpublishsし、フォームがsubscribedを受け取ります通知とそれに応じて反応することができます。

Gui-sのほかに、このメカニズムはすべての親子関係で使用できます

0
k3b