オブザーバーパターン。何が変わったのか知っていますか?


10

古典的なObserverパターンインターフェイスを定義する2つの抽象クラスSubjectとObserverを作成しました。オブザーバーパターンを実装するためにそれらから派生しています。オブザーバーは次のようになります。

void MyClass::Update(Subject *subject)
{
    if(subject == myService_)
    {
        DoSomething();
    }
    else if(subject == myOtherService_)
    {
        DoSomethingElse();
    }
}

これは問題なく、が何か変更したかがわかります。ただし、何が変わったのはわかりません。サブジェクトに最新のデータを照会するだけでよい場合もありますが、サブジェクトで何が変更されたかを正確に知る必要がある場合もあります。Javaでは、おそらく何が変更されたかの詳細を指定するために、notifyObservers()メソッドとnotifyObservers(Object arg)メソッドの両方があることに気づきました。

私の場合、サブジェクトでいくつかの異なるアクションの1つが発生したかどうかを知り、それが特定のアクションである場合は、そのアクションに関連する整数を知る必要があります。

だから私の質問は:

  1. (Javaのように)汎用的な引数を渡すC ++の方法は何ですか?
  2. オブザーバーは最高のパターンですか?多分ある種のイベントシステム?

更新

Observerパターンのテンプレート化について説明しているこの記事を見つけました:テンプレートを使用したSubject / Observerパターンの実装。これは、あなたが議論をテンプレート化できるかどうか疑問に思いました。

引数のテンプレート化に関するこのスタックオーバーフローの質問が見つかりました:テンプレートベースのサブジェクトオブザーバーパターン-static_castまたはdynamic_castを使用する必要があります。しかし、OPには誰も答えていない問題があるようです。

もう1つの方法は、Updateメソッドを変更して、次のようにEventArgオブジェクトを取得することです。

void MyClass::Update(Subject *subject, EventArg arg)
{
  ...

次に、特定の引数データ用にEventArgのサブクラスを作成し、それをupdateメソッド内の特定のサブクラスにキャストし直したと思います。

アップデート2

また、非同期メッセージベースのc ++フレームワークの作成に関する記事も見つかりました何が変わったについての詳細を被験者に伝えることを論じるパート2

Boost.Signalsの使用を真剣に検討しています。自分のオブザーバーパターンを使用することは、それが単純な場合には理にかなっていますが、型と引数のテンプレート化は複雑になり始めています。そして、Boost.Signals2のスレッドセーフが必要になる場合があります。

アップデート3

オブザーバーパターンに関する興味深い記事もいくつか見つかりました。

ハーブサッターによるオブザーバーの一般化

C ++でのオブザーバーパターンの実装-パート1

Observerデザインパターンの実装の経験(パート2)

Observerデザインパターンの実装の経験(パート3)

しかし、私はBoost.Signalsを使用するように実装を切り替えました。Boost.Signalsは、目的のために少し肥大化している可能性がありますが、正常に機能しています。そして、おそらく膨らみや速度の懸念は無関係です。


本文の質問は、実際のタイトルと一致していないようです。本当に問題の核心はどれですか?
ニコール

@Renesis:私は現在、投稿の冒頭のコードサンプルのように、Observerパターンを使用しています。私が現在取り組んでいるコードの場合、それに応じて対応できるように、何が変更されたかを具体的に知る必要があることがわかりました。現在の標準のオブザーバーパターンの実装では、この情報は提供されません。私の質問は、何が変更されたかに関する情報を最もよく取得する方法です。
ユーザー

@Renesis:私と同様の質問をするフォーラムスレッドは次のとおり
User

広告の更新2:Boost.Signalsの使用を確実にサポートします。自分でロールするよりもずっと便利です。このタスクのためだけに軽量化したい場合は、libsigc ++もあります(Boost.Signalsは、Boostの他の部分から多くのコードを使用します)。ただし、スレッドセーフではありません。
Jan Hudec、2011

速度の問題について:Boost.Signalsがどれほど高速かはわかりませんが、これは、大量のイベントが飛んでいる場合にのみ問題になります...
Max

回答:


4

かどうかはC ++やJAVA、観察者に通知することができますの情報と一緒に来て何を変更しました。同じメソッドnotifyObservers(Object arg)もC ++でも使用できます。

一般に、問題は残り、1人または複数のオブザーバーに複数のサブジェクトがディスパッチされる可能性があるため、class argハードコーディングできません。

通常、最善の方法は、さまざまなクラスに対して同じデータ型を形成する一般的なメッセージ/トークンの形式で引数を作成することですが、値は観測されるクラスによって異なります。あるいは、すべてのそのような通知値が、すべてに共通のいくつかのベースクラスのクラスから導出された場合。

オブザーバーパターンの場合、Argデータ型がオブザーバーとオブザーバー間でハードコーディングされていないこと重要です。それ以外の場合は、カップリングが原因で物事の進化が難しくなります。

編集
オブザーバーが観察するだけでなく、変更点に基づいて他の多くのタスクを実行する必要がある場合は、訪問者パターンを確認することもできます。ビジターパターンでは、オブザーバー/ビジターが変更されたオブジェクトを呼び出すため、変更が何であるかを知るだけでなく実際にそれを処理できます。


5
観察者が引数を解釈した場合は、そこにあるカップリング、あなたはタイプを隠すどんなに。実際、私は、特定の型を渡すよりも(、または同様の汎用)を渡す場合に進化するの難しいと言います。特定の型を使用すると、コンパイル時に何かが変わったのがわかりますが、一般的な型ではオブザーバーは渡された実際のデータを操作できないため、コンパイルして動作を停止します。Objectvoid *boost::any
Jan Hudec、2011

@JanHudec:私はそれに同意しますが、それは特定の1回限りのオブザーバー/サブジェクトサブクラスを各引数(つまり、各ユースケース)に設定することを意味しますか?
ユーザー

@JanHudec:カップリングも一方通行です。対象はオブザーバーについて何も知りません。はい、オブザーバーは主題について知っていますが、オブザーバーのパターンが機能する方法ではありませんか?
ユーザー

1
@ユーザー:はい、私は各サブジェクトに特定のインターフェースを作成し、各オブザーバーは観察する必要があるサブジェクトのインターフェースを実装します。まあ、私が使用するすべての言語には、言語またはフレームワークにバインドされたメソッドポインター(C#デリゲート、C ++ 11 std::functionBoost boost::function、Gtk + GClosure、Pythonバインドメソッドなど)があるため、適切なシグネチャでメソッドを定義し、システムに実際の観察者。結合は確かに1つの方法にすぎません。サブジェクトはオブザーバーのインターフェースを定義しますが、その実装については知りません。
Jan Hudec、2011

0

C ++で「Java Like」汎用イベント引数を送信する方法はいくつかあります。

1)イベント引数をvoid *として宣言し、イベントハンドラーの適切なクラスにキャストします。

2)次のような新しいクラス/インターフェースへのポインター/参照としてイベント引数を宣言します(例に応じて)

class GenericEventArgument
{
  virtual bool didAction1Happen() = 0;
  virtual int getActionInteger() = 0;
};

そして、実際のイベント引数クラスがこのクラスから派生するようにします。

テンプレートベースのオブザーバーに関するあなたの質問はチェックアウトしてください

http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ


-1

これが正規の名前であるかどうかはわかりませんが、昔のSmalltalkの時代から、オブザーバブルで何が変更されたかを識別するために「アスペクト」という用語を覚えています。

EventArg(およびサブクラス化)のアイデアほど複雑ではありません。オブザーバブルからオブザーバーに文字列(整数定数であってもかまいません)を渡すだけです。

プラス:2つの単純な方法(update(observable)およびupdateAspect(observable, aspect)

マイナス:オブザーバーは、オブザーバブルに詳細情報(つまり、「整数」)を尋ねる必要があるかもしれません

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