行为型模式: 关注系统中对象之间的相互交互,研究运行时对象之间的相互通信和协作,明确对象职。
观察者模式
建立对象间的一对多关系,使一个对象变化被所有关联对象感知。
角色
- 抽象主题(Subject):
它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。 - 具体主题(Concrete Subject):
将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。 - 抽象观察者(Observer):
为所有的具体观察者定义一个接口,在得到主题通知时更新自己。 - 具体观察者(Concrete Observer):
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
其中,主题提供了注册事件、销毁事件、通知方式等方法。
而抽象观察者是所有观察者的父类,我们添加新的观察者只需要继承抽象观察者类即可。
抽象主题类是所有通知者的父类,我们添加新的通知者只需要继承抽象主题类即可。
#include <iostream>
#include <list>
using namespace std;
struct MyObserver // 观察者
{
virtual void response() = 0; // 相应方式
};
class Mouse : public MyObserver // 老鼠
{
public:
void response() { cout << "老鼠拼命的逃包" << endl; }
};
class Dog : public MyObserver // 狗
{
public:
void response() { cout << "狗跟着叫 ! " << endl; }
};
class MySubject // 主题
{
protected:
std::list<MyObserver*> obslist;
public:
void attach(MyObserver* obs)
{
obslist.push_back(obs);
}
void detach(MyObserver* obs)
{
obslist.remove(obs);
}
public:
virtual void cry() = 0;
};
// 通知者
class Cat : public MySubject
{
public:
void cry()
{
cout << "猫叫 !" << endl;
cout << "----------" << endl;
for (auto& x : obslist)
{
x->response();
}
}
};
int main() // client
{
MySubject* sp = new Cat();
MyObserver* oba = new Mouse();
MyObserver* obb = new Dog();
// 建立猫与{狗、鼠}之间的关系
sp->attach(oba);
sp->attach(obb);
sp->cry();
// 现在不让狗得到此事件的通知
sp->detach(oba);
sp->cry();
return 0;
}
示例2:
/*
秘书通知员工模型
秘书:吃饭 员工:收到,去吃饭
秘书:boss到 员工:收到,马上努力工作
*/
#include <iostream>
#include <string>
#include <list>
class Subject; // 事件中心,通知人的抽象类,负责通知观察者
class Observer // 观察者抽象类
{
protected:
std::string name; // 员工姓名
Subject* sub; // 关联事件中心——秘书,根据事件中心的不同事件作出不同的应对策略
public:
Observer(std::string nm, Subject* s)
:name(nm), sub(s) // 构造函数
{}
virtual ~Observer() {} // 虚析构,注:多态继承中基类一定要写上虚析构
// 事件通知后的响应方式
virtual void update() const = 0; // 纯虚函数
};
class StockObserver :public Observer // 摸鱼:玩股票的员工
{
public:
StockObserver(std::string nm, Subject* s)
:Observer(nm, s)
{}
void update() const override;
};
class NBAObserver : public Observer
{
public:
NBAObserver(std::string nm,Subject* s)
:Observer(nm,s)
{}
void update()const override;
};
// 事件中心类,通知人的抽象类
class Subject
{
public:
std::string event; // 事件
protected:
std::list<Observer*> observer; // 观察者列表
public:
virtual ~Subject() {}
// 添加函数,把对应通知对象添加到列表中
virtual void attach(Observer*) = 0; // 添加关联
virtual void detach(Observer*) = 0; // 解除关联
virtual void notify() = 0; // 通知
};
class Secretary : public Subject
{
virtual void attach(Observer* ob) override
{
observer.push_back(ob);
}
virtual void detach(Observer* ob) override
{
observer.remove(ob);
}
virtual void notify() override
{
for (auto& x : observer)
x->update(); // 调用他们各自的消息处理函数
}
};
/* -------------- 响应方式实现 -------------------*/
void StockObserver::update() const
{
if (sub->event == "吃饭")
{
std::cout << "收到消息:" << sub->event << ",我马上去吃饭!" << std::endl;
}
else if (sub->event == "boss来了")
{
std::cout << "收到消息:" << sub->event << ",我马上关闭股票,努力工作!" << std::endl;
}
}
void NBAObserver::update() const
{
if (sub->event == "吃饭")
{
std::cout << "收到消息:" << sub->event << ",我马上去吃饭!" << std::endl;
}
else if (sub->event == "boss来了")
{
std::cout << "收到消息:" << sub->event << ",我马上关闭NBA ,努力工作!" << std::endl;
}
}
int main()
{
// 创建通知人
Subject* dwp = new Secretary();
// 创建员工
Observer* xm = new StockObserver("小明", dwp);
Observer* dz = new StockObserver("大壮", dwp);
Observer* zg = new NBAObserver("张刚", dwp);
// 绑定观察关系
dwp->attach(xm);
dwp->attach(dz);
dwp->attach(zg);
// 注:这里的时间类型可通过枚举实现
// 确定事件类型
dwp->event = "吃饭";
// 通知事件给绑定的观察者
dwp->notify();
// 确定事件类型
dwp->event = "boss来了";
dwp->notify();
delete dwp;
delete xm;
delete dz;
delete zg;
return 0;
}
上述代码并不完美,如果我们能实现使用智能指针自动回收内存就很完美了。
这里我们只是在使用时用到了智能指针,更好的办法是将观察者模式设计成含有智能指针的版本。
int main()
{
// 创建通知人
//std::unique_ptr<Subject> dwp(new Secretary());
//std::shared_ptr<Subject> dwp = std::make_shared<Secretary>();
std::shared_ptr<Subject> dwp(new Secretary());
// 创建员工
std::unique_ptr<Observer> xm(new StockObserver("小明", dwp.get()));
std::unique_ptr<Observer> dz(new StockObserver("大壮", dwp.get()));
std::unique_ptr<Observer> zg(new NBAObserver("张刚", dwp.get()));
// 绑定观察关系
dwp->attach(xm.get());
dwp->attach(dz.get());
dwp->attach(zg.get());
// 注:这里的时间类型可通过枚举实现
// 确定事件类型
dwp->event = "吃饭";
// 通知事件给绑定的观察者
dwp->notify();
// 确定事件类型
dwp->event = "boss来了";
dwp->notify();
return 0;
}