私は汎用オブザーバー メカニズムを従来の C++ アプリケーションに追加するのに忙しくしています (Visual Studio 2010 を使用していますが、.Net は使用していないため、.Net デリゲートは問題外です)。
設計では、アプリケーション固有の部分を一般的なオブザーバー メカニズムからできるだけ分離したいと考えています。
オブザーバーを実装する最も論理的な方法は、次のようになります。
class IDoThisObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
オブザーバーのタイプ (IDoThisObserver、IDoThatObserver、...) ごとに、メソッド (handleDoThis、handleDoThat) の引数は異なります。
次のように、オブザーバーを格納する一般的な方法に残っているもの:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
オブザーバーの呼び出しは、オブザーバーの種類ごとに引数が異なるため、一般化できません。
別の方法は、次のように、すべての引数を 1 つの引数に「パック」することです。
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
次に、次のように、より一般的なオブザーバーを定義します。
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
そして、これらのオブザーバーのコレクションは次のようになります。
template<typename T>
class ObserverContainer
{
public:
void addObserver (IObserver<T> &obs) {m_observers.push_back(&obs);}
private:
std::list<IObserver<T>*> m_observers;
};
これで、すべてのオブザーバーの呼び出しなど、さらに多くのロジックをこの ObserverContainer に一元的に追加できます。呼び出しの「開始者」は、通知構造を作成して入力するだけで済みます。
複数の種類のオブザーバーから継承したいクラスは、次のようにする必要があります。
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
これらのアプローチ (複数の明示的な引数を持つオブザーバーまたは 1 つの構造体引数を持つオブザーバー) のどれが最適だと思われますか? これらのアプローチのいずれかに利点または欠点はありますか?
編集:私は代替アプローチをもう少し調べました.Slot/Signalアプローチは別の良い候補のようです. Slot/Signal に知っておくべき重要な欠点はありますか?