C++ と STL を使用して Observer パターンを実装しているときに、興味深い問題に遭遇しました。この古典的な例を考えてみましょう:
class Observer {
public:
virtual void notify() = 0;
};
class Subject {
public:
void addObserver( Observer* );
void remObserver( Observer* );
private:
void notifyAll();
};
void Subject::notifyAll() {
for (all registered observers) { observer->notify(); }
}
この例は、デザイン パターンに関するすべての本に記載されています。残念ながら、実際のシステムはもっと複雑なので、ここに最初の問題があります。一部のオブザーバーは、通知を受けると、サブジェクトに他のオブザーバーを追加することを決定します。これにより、使用する「for」ループとすべての反復子が無効になります。解決策はかなり簡単です。登録されたオブザーバー リストのスナップショットを作成し、スナップショットを反復処理します。新しいオブザーバーを追加してもスナップショットは無効にならないため、すべて問題ないようです。しかし、ここで別の問題が発生します。オブザーバーは、通知を受けると自分自身を破壊することを決定します。さらに悪いことに、1 つのオブザーバーが他のすべてのオブザーバーを破棄することを決定でき (それらはスクリプトから制御されます)、キューとスナップショットの両方が無効になります。割り当て解除されたポインターを繰り返し処理していることに気づきました。
私の質問は、オブザーバーが互いに殺し合う状況をどのように処理すればよいですか? すぐに使えるパターンはありますか? 「オブザーバー」は世界で最も簡単なデザインパターンだとずっと思っていましたが、今ではそれを正しく実装するのはそれほど簡単ではないようです...
皆様、ご興味をお持ちいただきありがとうございます。決定の要約を見てみましょう。
[1]「やらないで」申し訳ありませんが、必須です。オブザーバーはスクリプトから制御され、ガベージ コレクションされます。ガベージ コレクションを制御して割り当て解除を防ぐことはできません。
[2] "boost::signal を使用する"最も有望な決定ですが、プロジェクトに boost を導入することはできません。そのような決定は、プロジェクト リーダーのみが行う必要があります (私たちは Playstation で書いています)。
[3] 「shared__ptr を使用する」これにより、オブザーバーの割り当て解除が防止されます。一部のサブシステムはメモリ プールのクリーンアップに依存している可能性があるため、shared_ptr を使用できないと思います。
[4] 「オブザーバーの割り当て解除を延期する」通知中にオブザーバーを削除するためにキューに入れ、2 番目のサイクルを使用してそれらを削除します。残念ながら、割り当て解除を防ぐことはできないため、オブザーバーをある種の「アダプター」でラップし、実際には「アダプター」のリストを保持するというトリックを使用します。デストラクタでは、オブザーバがアダプタから割り当てを解除し、2 番目のサイクルで空のアダプタを破棄します。
ps 質問を編集してすべての投稿を要約してもよろしいですか? 私はStackOverflowの初心者です...