首先,观察者模式如下定义:
观察者模式(别名:依赖,发布-订阅)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都得到通知并被自动更新。
英文解释:Observer Pattern(Another Name: Dependents, Publish-Subscribe) Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically..
在许多设计中,经常涉及到多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化。而观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。具体地, 一个图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离,定义应用数据的类和负责界面表示的类可以各自独立地复用。当然它们也可一起工作。一个表格对象和一个柱状图对象可使用不同的表示形式描述同一个应用数据对象的信息。表格对象和柱状图对象互相并不知道对方的存在,这样使你可以根据需要单独复用表格或柱状图。但在这里是它们表现的似乎互相知道。当用户改变表格中的信息时,柱状图能立即反映这一变化, 反过来也是如此。MFC中的view/doc结构正是这种思想。
面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了运用程序的可维护性和重用性。
比如,某些寻找工作的人对“求职中心”和“人才市场”的职业需求信息的变化非常关心,很想跟踪“求职中心”和“人才市场”中职业需求信息的变化。“求职者”可以让“求职中心”把自己登记下来,这样求职中心就会及时通知她最新的职业信息需求。
观察者模式的结构中包括四种角色:主题(Subject); 观察者(Observer) ; 具体主题(ConcreteSubject) ; 具体观察者(ConcreteObserver)
观察者模式的类图
根据上面的求职的例子,我们假设有两个被观察者,“工作中心”和“人才市场”,两个观察者,“学生”和“老师”,学生和老师在找工作,他们把自己的信息注册给“工作中心”和“人才市场”,一旦“工作中心”或者“人才市场”有工作信息,“学生”和“老师”都能收到该信息。
实现C++代码如下:
#pragma warning(disable : 4786) #include <iostream> #include <set> #include <string> using namespace std; /////////////////////抽象模式定义 class CSubject; //观察者,纯虚基类,做接口用 class CObserver { public: CObserver::CObserver(){}; virtual CObserver::~CObserver(){}; //当被观察的目标发生变化时,通知调用该方法 //来自被观察者pObs, 扩展参数为pArg virtual void Update(CSubject* pObs, void* pArg = NULL) = 0; }; //被观察者,即Subject class CSubject { public: CSubject() : m_bChanged(false) {}; virtual ~CSubject() {}; //注册观察者 void Attach(CObserver* pObs); //注销观察者 void Detach(CObserver* pObs); //注销所有观察者 void DetachAll(); //若状态变化,则遍历观察者,逐个通知更新 void Notify(void* pArg = NULL); //测试目标状态是否变化 bool HasChanged(); //获取观察者数量 int GetObserversCount(); protected: //设置状态变化!!!必须继承CSubject才能设置目标状态 void SetChanged(); //初始化目标为未变化状态 void ClearChanged(); private: bool m_bChanged; //状态 set<CObserver*> m_setObs; //set保证目标唯一性 }; /////////////////////抽象模式实现 void CSubject::Attach(CObserver* pObs) { if (!pObs) return; m_setObs.insert(pObs); } void CSubject::Detach(CObserver* pObs) { if (!pObs) return; m_setObs.erase(pObs); } void CSubject::DetachAll() { m_setObs.clear(); } void CSubject::SetChanged() { m_bChanged = true; } void CSubject::ClearChanged() { m_bChanged = false; } bool CSubject::HasChanged() { return m_bChanged; } int CSubject::GetObserversCount() { return m_setObs.size(); } void CSubject::Notify(void* pArg /* = NULL */) { if (!HasChanged()) return; cout << "notify observers…" << endl; ClearChanged(); set<CObserver*>::iterator itr = m_setObs.begin(); for (; itr != m_setObs.end(); itr++) { (*itr)->Update(this, pArg); } } /////////////////////具体应用类定义和实现 //jobcenter是发布者,即被观察者(subject) class CJobCenter : public CSubject { public: void Publish(const string &strContent) { cout << "jobcenter publish, content: " << strContent << endl; SetChanged(); Notify(const_cast<char*>(strContent.c_str())); } }; //TalentMarket是发布者,即被观察者(subject) class CTalentMarket : public CSubject { public: void Publish(const string &strContent) { cout << "TalentMarket publish, content: " << strContent << endl; SetChanged(); Notify(const_cast<char*>(strContent.c_str())); } }; //学生,观察者 class CStudent : public CObserver { public: CStudent(const string &strName) : m_strName(strName){} virtual void Update(CSubject* pObs, void* pArg = NULL) { char* pContent = static_cast<char*>(pArg); //观察多个目标 if (dynamic_cast<CJobCenter*>(pObs)) { cout << m_strName << " updated from jobcenter, content: " << pContent << endl; } else if (dynamic_cast<CTalentMarket*>(pObs)) { cout << m_strName << " updated from TalentMarket, content: " << pContent << endl; } } private: string m_strName; }; //老师,观察者 class CTeacher : public CObserver { public: CTeacher(const string &strName) : m_strName(strName){} virtual void Update(CSubject* pObs, void* pArg = NULL) { char* pContent = static_cast<char*>(pArg); if (dynamic_cast<CJobCenter*>(pObs)) { cout << m_strName << " updated from jobcenter, content: " << pContent << endl; } if (dynamic_cast<CTalentMarket*>(pObs)) { cout << m_strName << " updated from TalentMarket, content: " << pContent << endl; } } private: string m_strName; }; /////////////////Main int main() { //目标(被观察者) CJobCenter* pJobCenter = new CJobCenter(); CTalentMarket* pTalentMarket = new CTalentMarket(); //观察者. 一个观察者可以观察多个目标 CStudent* pStudent = new CStudent("stu reader"); CTeacher* pTeacher = new CTeacher("tea reader"); pJobCenter->Attach(pStudent); //jobcenter注册观察者 pJobCenter->Attach(pTeacher); //jobcenter注册观察者 pTalentMarket->Attach(pStudent); //TalentMarket注册观察者 pTalentMarket->Attach(pTeacher); //TalentMarket注册观察者 //工作中心发布信息 pJobCenter->Publish("工作中心发布一个IT工作信息"); cout << endl; //人才市场发布信息 pTalentMarket->Publish("人才市场发布一个销售工作信息"); cout << "\nTalentMarket detached tea reader" << endl; pTalentMarket->Detach(pTeacher); cout << "TalentMarket observers count: " << pTalentMarket->GetObserversCount() << endl << endl; pTalentMarket->Publish("人才市场发布一个管理工作信息"); system("pause"); return 0; }
观测者模式的优点:
1,具体主题和具体观察者是松耦合关系。由于主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(subject)接口的某个类的实例,但不需要知道具体是哪个类。
2,观察模式满足“开-闭原则”。主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,我们就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题(Observer)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。
参考:http://baike.baidu.com/view/1854779.htm