オブザーバーモデル/ビュー/コントロール(MVC)構造のインスタンスは、システム開発アーキテクチャの設計において非常に重要な位置と重要性を持っているため、オブザーバーモードは最も広く使用され影響力のあるモードの1つである必要があります。MVCはビジネスロジックを実装しますプレゼンテーションからの分離層。個人的には、オブザーバー モードはソフトウェア開発プロセスで習得して使用する必要のあるモードの1つだと思い ます 。MFCでは、Doc / View(ドキュメントビュー構造)がMVCを実装するためのフレームワーク構造を提供します(残念ながら、デザインパターン(オブザーバーパターン)の観点からDoc / Viewを分析する記事がさらに書かれています:))。Javaのラインナップでは、StrutsはMFCのDoc / View構造と同様のMVCを実装するためのフレームワークを提供します。さらに、Java言語自体が、オブザーバーパターンの実装インターフェイスを提供します。これについては、後で説明します。
もちろん、MVCはオブザーバーパターンの一例にすぎません。オブザーバーモデルによって解決される問題は、1(サブジェクト)から多(オブザーバー)への依存関係を確立し、「1つ」が変更されたときに「1つ」に依存する多くのユーザーも同時に変更できるようにすることです。 。最も一般的な例は次のとおりです。同じデータセットで統計分析を実行する場合、複数の形式の表現を提供したいと考えています(たとえば、テーブルでの統計表示、ヒストグラムの統計表示、パーセンテージ統計表示など)。これらの表現はすべて同じデータセットに依存しています。もちろん、データが変更されると、すべての統計表示を同時に変更できます。オブザーバーモデルはこの問題を解決します。
ここでのターゲットサブジェクトは、それに依存するオブザーバーオブザーバーの登録(アタッチ)および登録解除(デタッチ)操作を提供し、それに依存するすべてのオブザーバーを同期する操作(通知)を提供します。オブザーバーオブザーバーは更新操作を提供します。オブザーバーがサブジェクトの状態を変更しても、ここでのオブザーバーの更新操作はそれ自体を更新しないことに注意してください。この更新操作は、サブジェクトがすべてのオブザーバーに変更を通知する通知を送信するまで遅延されます( Updateを呼び出します)。
1.オブザーバーパターンとは
多くの設計では、複数のオブジェクトが特定のオブジェクトのデータ変更に関心を持っていることが多く、これらの複数のオブジェクトはすべて、その特定のオブジェクトのデータ変更を追跡する必要があります。つまり、オブジェクト間に1対多の関係がある場合です。この場合、オブザーバーモードを使用できます。オブジェクトが変更されると、その依存オブジェクトに自動的に通知されます。
オブザーバーモードは、複数のオブジェクトがオブジェクト内のデータの変化を知りたい成熟したモードです。オブザーバーパターンには、「theme」というオブジェクトと「observers」というオブジェクトがいくつかあり、「theme」の状態が変化した場合、「theme」と「observer」の間には1対多の依存関係があります。発生すると、すべての「オブザーバー」に通知されます。
主な解決策:オブジェクトの状態が変化したときに他のオブジェクトに通知する問題、および高度なコラボレーションを保証するには、使いやすさと低結合を考慮する必要があります。
第二に、オブザーバーモデルの構造
オブザーバーパターンの構造には、次の4つの役割があります。
(1)サブジェクト(サブジェクト):サブジェクトは、オブザーバーの追加と削除、オブザーバーへのデータ更新の通知など、特定のサブジェクトが実装する必要のあるメソッドを指定するインターフェースです。
(2)オブザーバー:オブザーバーは、特定のオブザーバーがデータを更新するために使用する方法を指定するインターフェースです。
(3)具象サブジェクト(ConcreteSubject):具象サブジェクトは、頻繁に変更される可能性のあるデータを含むサブジェクトインターフェイスを実装するクラスのインスタンスです。特定のトピックでは、ArrayListなどのコレクションを使用してオブザーバー参照を格納する必要があります。これにより、データが変更されたときに特定のオブザーバーに通知できます。
(4)具象オブザーバー(ConcreteObserver):具象オブザーバーは、オブザーバーインターフェイスを実装するクラスのインスタンスです。特定のオブザーバーには、特定のテーマ参照を格納できるテーマインターフェイス変数が含まれているため、特定のオブザーバーは、特定のトピックに特定のトピックのコレクションへの参照を追加したり、自分自身をオブザーバーにしたり、この特定のトピックを[特定のコレクションから削除]から変更したりできます。あなたがもはやそれのオブザーバーではないようにトピック。
三、オブザーバーモードの使用
(1)オブジェクトのデータが更新されると、他のオブジェクトに通知する必要がありますが、このオブジェクトは通知されたオブジェクトと緊密に結合されることを望んでいません。
(2)オブジェクトのデータが更新されると、このオブジェクトは他のオブジェクトにも自分のデータを更新させる必要がありますが、このオブジェクトはデータを更新する必要のあるオブジェクトの数を認識していません。
オブザーバーモードは、実際のプロジェクトアプリケーションでは非常に一般的です。たとえば、ATMマシンからお金を引き出し、間違ったパスワードを何度も入力すると、カードはATMに飲み込まれます。飲み込みアクションが発生すると、どのようなイベントがトリガーされますか?最初のカメラは継続的なスナップショットを撮ります。2番目はカードの飲み込みが発生したことを監視システムに通知します。3番目はATM画面を初期化して初期状態に戻ります。ATM全体が使用できないという理由だけでカードを飲み込むことはできません。 、そうですか?両方のアクションはオブザーバーモードを介して実行されます。オブザーバーはメッセージをブロードキャストでき、1つのメッセージが複数のイベントをトリガーできます。これはオブザーバーモードの非常に重要な機能です。
オブザーバーモードを使用する場合、解決すべき2つの重要な問題もあります。
放送チェーンの問題。
データベーストリガーを実行した場合は、トリガーチェーンの問題があることを知っておく必要があります。たとえば、トリガーはテーブルAに書き込まれ、内容は、フィールドが更新された後にテーブルBのデータを更新することです。 Bにもトリガーがあり、テーブルCを更新するために、テーブルCにもトリガーがあります...これで、このデータベースは基本的に破棄されます。私たちのオブザーバーモデルにも同じ問題があります。オブザーバーは二重のアイデンティティを持つことができます。オブザーバーも観察されます。これは問題ありませんが、チェーンが確立されると、ロジックがより複雑になり、保守性が非常に悪くなります。経験によるとオブザーバーモードの最大で1つのオブジェクトがオブザーバーとオブザーバーの両方であることを示します。つまり、メッセージは最大で1回転送されます(2回送信されます)。これは制御に適しています。
問題を非同期的に処理します。
オブザーバーにはアクションがあり、オブザーバーは応答する必要があります。オブザーバーが多く、処理時間が長い場合はどうなりますか?次に、非同期を使用します。非同期処理では、スレッドセーフとキューの問題を考慮する必要があります。メッセージキューを確認する時間があれば、理解を深めることができます。
第四に、オブザーバーモデルの長所と短所
利点:
1.特定の対象と特定のオブザーバーは疎結合です。テーマインターフェイスはオブザーバーインターフェイスにのみ依存するため、特定のテーマは、そのオブザーバーがオブザーバーインターフェイスを実装する特定のクラスのインスタンスであることのみを認識しますが、それがどのクラスであるかを知る必要はありません。同様に、オブザーバーはテーマインターフェイスのみに依存するため、特定のオブザーバーは、依存するテーマがテーマインターフェイスを実装する特定のクラスのインスタンスであることのみを認識しますが、それがどのクラスであるかを知る必要はありません。
2.オブザーバーモードは「開閉原理」を満たしています。テーマインターフェイスはオブザーバーインターフェイスのみに依存します。このように、特定のテーマを作成するクラスはオブザーバーインターフェイスのみに依存します。したがって、オブザーバーインターフェイスを実装する新しいクラスを追加する場合、コードを変更する必要はありません。特定のテーマを作成するクラスの。。同様に、特定のオブザーバーを作成するクラスは、テーマインターフェイスのみに依存します。テーマインターフェイスを実装する新しいクラスを追加する場合、特定のオブザーバークラスを作成するためのコードを変更する必要はありません。
短所:
1.観測対象に直接および間接の観測者が多い場合、すべての観測者に通知するのに時間がかかります。
2.オブザーバーとオブザベーションターゲットの間に循環依存関係がある場合、オブザーバーターゲットはそれらの間で循環呼び出しをトリガーし、システムがクラッシュする可能性があります。
3.オブザーバーモードには、観察対象がどのように変化したかを観察者に知らせる対応するメカニズムはありませんが、観察対象が変化したことだけを知っています。
五、オブザーバーモードの実現
オブザーバークラス---抽象オブザーバーは、すべての特定のオブザーバーのインターフェイスを定義し、サブジェクトから通知されたときに自身を更新します。
このインターフェースは更新インターフェースと呼ばれ、抽象オブザーバーは通常、抽象クラスまたはインターフェースによって実装されます。更新インターフェイスには通常、更新メソッドと呼ばれる更新メソッドが含まれています。
#pragma once
//Subject.h
#include <list>
#include <string>
using namespace std;
typedef string State;
class Observer;
class Subject
{
public:
virtual ~Subject();
virtual void Attach(Observer* obv);
virtual void Detach(Observer* obv);
virtual void Notify();
virtual void SetState(const State& st) = 0;
virtual State GetState() = 0; protected:
Subject();
private:
list<Observer* >* _obvs;
};
class ConcreteSubject:public Subject
{
public:
ConcreteSubject();
~ConcreteSubject();
State GetState();
void SetState(const State& st);
protected:
private:
State _st;
};
//Subject.cpp
#include "stdafx.h"
#include "Subject.h"
#include "Observer.h"
#include <iostream>
#include <list>
using namespace std;
typedef string state;
Subject::Subject()
{
//****在模板的使用之前一定要 new,创建
_obvs = new list<Observer*>;
}
Subject::~Subject()
{
}
void Subject::Attach(Observer* obv)
{
_obvs->push_front(obv);
}
void Subject::Detach(Observer* obv)
{
if (obv != NULL)
_obvs->remove(obv);
}
void Subject::Notify()
{
list<Observer*>::iterator it;
it = _obvs->begin();
for (;it != _obvs->end();it++)
{
//关于模板和 iterator 的用法
(*it)->Update(this);
}
}
ConcreteSubject::ConcreteSubject()
{
_st = '\0';
}
ConcreteSubject::~ConcreteSubject()
{
}
State ConcreteSubject::GetState()
{
return _st;
}
void ConcreteSubject::SetState(const State& st)
{
_st = st;
}
#pragma once
//Observer.h
#include "Subject.h"
#include <string>
using namespace std;
typedef string State;
class Observer
{
public:
virtual ~Observer();
virtual void Update(Subject* sub) = 0;
virtual void PrintInfo() = 0;
protected:
Observer();
State _st;
private:
};
class ConcreteObserverA:public Observer
{
public:
virtual Subject* GetSubject();
ConcreteObserverA(Subject* sub);
virtual ~ConcreteObserverA();
//传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
void Update(Subject* sub);
void PrintInfo();
protected:
private:
Subject* _sub;
};
class ConcreteObserverB:public Observer
{
public:
virtual Subject* GetSubject();
ConcreteObserverB(Subject* sub);
virtual ~ConcreteObserverB();
//传入 Subject 作为参数,这样可以让一个 View 属于多个的 Subject。
void Update(Subject* sub);
void PrintInfo();
protected:
private:
Subject* _sub;
};
//Observer.cpp
#include "stdafx.h"
#include "Observer.h"
#include "Subject.h"
#include <iostream>
#include <string>
using namespace std;
Observer::Observer()
{
_st = '\0';
}
Observer::~Observer()
{
}
ConcreteObserverA::ConcreteObserverA(Subject* sub)
{
_sub = sub;
_sub->Attach(this);
}
ConcreteObserverA::~ConcreteObserverA()
{
_sub->Detach(this);
if (_sub != 0)
{
delete _sub;
}
}
Subject* ConcreteObserverA::GetSubject()
{
return _sub;
}
void ConcreteObserverA::PrintInfo()
{
cout<<"ConcreteObserverA observer.... "<<_sub->GetState()<<endl;
}
void ConcreteObserverA::Update(Subject* sub)
{
_st = sub->GetState();
PrintInfo();
}
ConcreteObserverB::ConcreteObserverB(Subject* sub)
{
_sub = sub;
_sub->Attach(this);
}
ConcreteObserverB::~ConcreteObserverB()
{
_sub->Detach(this);
if (_sub != 0)
{
delete _sub;
}
}
Subject* ConcreteObserverB::GetSubject()
{
return _sub;
}
void ConcreteObserverB::PrintInfo()
{
cout<<"ConcreteObserverB observer.... "<<_sub->GetState()<<endl;
}
void ConcreteObserverB::Update(Subject* sub)
{
_st = sub->GetState();
PrintInfo();
}
int main(int argc, _TCHAR* argv[])
{
ConcreteSubject* sub = new ConcreteSubject();
Observer* o1 = new ConcreteObserverA(sub);
Observer* o2 = new ConcreteObserverB(sub);
sub->SetState("old");
sub->Notify();
sub->SetState("new"); //也可以由 Observer 调用
sub->Notify();
return 0;
}
オブザーバーパターンの実装では、Subjectはすべてのオブザーバーを格納するためのコンテナーとしてリストを維持します。Notify操作が呼び出されるたびに、リスト内のObserverオブジェクトをトラバースし、通知をブロードキャストして状態を変更します(ObserverのUpdate操作を呼び出します)。ターゲットの状態は、サブジェクト自体によって変更することも(例)、オブザーバーの操作によって変更することもできます(サブジェクトのSetState操作を呼び出すことができます)。通知操作は、サブジェクトターゲット(例)によってアクティブにブロードキャストすることも、オブザーバーによって呼び出すこともできます(オブザーバーがサブジェクトへのポインターを維持するため)。
サンプルプログラムを実行すると、サブジェクトが「古い」状態の場合、それに依存する2人のオブザーバーはすべて「古い」と表示され、ターゲットの状態が「新しい」に変わると、依存する2人のオブザーバーが表示されることがわかります。また、「新規」に変更されます。
6、オブザーバーモードとコミッションの組み合わせ
上記のコードは依存性逆転の原則を使用していますが、「抽象インフォーマー」は依然として「抽象オブザーバー」に依存しています。つまり、抽象オブザーバーなどのインターフェースがない場合、通知関数を送信できません。
もう1つは特定のオブザーバーごとのものであり、必ずしもUpdateメソッド呼び出しである必要はありません。
目的:通知者とオブザーバーはお互いをまったく知らず、クライアントは誰に通知するかを決定します
セブン、まとめ
オブザーバーモードを実装するときは、オブザーバーとオブザーバーオブジェクト間のインタラクティブな関係をクラス間の直接呼び出しとして具体化できないことに注意してください。そうしないと、オブザーバーとオブザーバーオブジェクトが密接に結合され、基本的に次の原則に違反します。オブジェクト指向設計。オブザーバーが観察対象を「観察」する場合でも、オブザーバーが自分の変更をオブザーバーに「通知」する場合でも、直接呼び出すべきではありません。