1 定义
观察者模式定义了一种一对多依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
2 UML类图及核心思想
观察者模式也叫做发布-订阅模式,比如我关注了某微信公众号,那么一旦公众号维护者进行了内容更新,就会把消息推送到各订阅者。当然如果订阅者感觉推送内容质量一般,则可以取消关注。
主题内部维护者一个观察者的列表;
观察者内部也拥有一个主题对象的指针或引用;
所有的观察者都实现了统一的接口(这里是update接口),以接收主题的数据更新;
如果观察者想订阅某服务,则在自己的构造函数内将自己添加到主题的观察者列表中,这是通过调用主题的Attach接口完成的;
如果观察者不想再订阅某项服务,则观察者从可以调用主题的Detach函数将自己从主题的观察者列表中删除;
具体的主题在进行新消息通知时,遍历当前的观察者列表,挨个调用观察者的update函数,把新消息推送给各观察者。
什么时候用观察者模式?
当一个对象的改变需要同时改变其他对象的时候,而且它不知道有多少对象需要改变,就需要考虑使用观察者模式。
为什么有抽象主题和抽象观察者的存在?
因为具体的主题可以有多个,比如公众号的消息可以是员工A发布的,也可以是员工B发布的,如果不引入抽象主题,那么观察者就需要在具体主题改变时修改代码,这样违背了开闭原则。对于主题而言同样如此,添加了新类型的观察者时也需要修改代码。
按照“针对接口编程,而不是针对实现编程的原则”,主题包含观察者的基类对象,这样主题就可以把消息推送给不同类型的观察者。观察者也包含主题的基类对象。
解除耦合:
观察者模式所做的工作实际上是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得一边的变换不会影响到另一边。
数据更新时的“推”和“拉”:
数据更新时,如果由主题遍历观察者列表中已注册的所有观察者,调用其update函数更新数据,是一种“推”的实现方式。这种方式的缺点是,如果主题推送的数据有所扩充,需要挨个修改推送给不同观察者的数据内容,代码可扩充性较差。
应该改为通过“拉”的方式进行数据更新,所谓“拉”即各观察者自己按照自己的需要从主题中获取数据,当然也需要主题提供了一系列的get接口允许观察者从自身获取不同类型的数据。这样的好处是,如果新增了观察者,或者某已有观察者想获取的数据发生了变化,主题的代码不需要修改,只需要修改发生了变化的或新增的观察者的实现即可,代码修改相对较少。
3 示例代码
//subject.h
#include <iostream>
#include<list>
#include "Observer.h"
class Subject
{
public:
Subject() {
}
virtual ~Subject() {
}
std::list<Observer*> m_Observer;
virtual void Attach(Observer* pObserver) =0;
virtual void Detach(Observer* pObserver) =0;
virtual void Notify() =0;
};
//ConcreteSubject.h
#include "Subject.h"
class ConcreteSubject : public Subject
{
public:
ConcreteSubject();
virtual ~ConcreteSubject();
virtual void Attach(Observer* pObserver);
virtual void Detach(Observer* pObserver);
virtual void Notify();
};
//ConcreteSubject.cpp
#include "ConcreteSubject.h"
ConcreteSubject::ConcreteSubject()
{
}
ConcreteSubject::~ConcreteSubject(){
}
void ConcreteSubject::Attach(Observer* pObserver)
{
m_Observer.push_back(pObserver);
}
void ConcreteSubject::Detach(Observer* pObserver)
{
for (std::list<Observer*>::iterator iter = m_Observer.begin(); iter != m_Observer.end(); )
{
if (*iter == pObserver)
{
std::cout << "Delete " << (*iter)->m_strName << std::endl;
iter = m_Observer.erase(iter);
break;
}
else
{
iter++;
}
}
}
void ConcreteSubject::Notify()
{
for (std::list<Observer*>::iterator iter = m_Observer.begin(); iter != m_Observer.end(); iter++)
{
(*iter)->Update();
}
}
//Observer.h
class Observer
{
public:
Observer(const char* pName) {
m_strName = pName;
}
virtual ~Observer() {
}
virtual void Update() =0;
const char* m_strName;
};
//ConcreteObserver.h
#include "Subject.h"
#include "Observer.h"
class ConcreteObserver : public Observer
{
public:
ConcreteObserver(const char* pName,Subject* pSubject);
virtual ~ConcreteObserver();
virtual void Update();
};
//ConcreteObserver.cpp
#include "ConcreteObserver.h"
ConcreteObserver::ConcreteObserver(const char* pName, Subject* pSubject):Observer(pName)
{
pSubject->Attach(this);
}
ConcreteObserver::~ConcreteObserver(){
}
void ConcreteObserver::Update(){
std::cout << "I am " << m_strName << ",I accept new information!" << std::endl;
}
运行结果: