观察者模式
最近重温的一部电视剧《李狗嗨》,是日剧,内容大概是律师处理案子。里面的第二部有一条主线,是审判一个民众都认为应该死亡的一个女人,因为这个女人靠男人过成富豪。最后的结局当然是......但是当审判长锤下的那一刻,最忙的反而是一些记者、写手等等,因为为了抢速度发行报纸和新闻播报等等。其实这个法庭可视为观察者模式,法官是被观察者,当被观察者做出判决的时候,观察者纷纷做出了自己的状态变化,并且去执行自己应该执行的操作。因此观察者模式就是,一对多的对象对应关系,当这个被观察者发生状态变化的时候,一系列的观察都会进行状态变化并且可能还需要执行一些操作。
Subject类:所有被观察的共同类,拥有的是观察者列表、然后进行添加和删除观察者的方法、一个用于刷新所有观察者对象状态的方法+各种实际方法
ConcreteSubject:具体被观察者,每个不同的被观察发生相同的状态变化的时候,所引起其观察者状态变化是不同的。不然法庭中的审判长判决和律师进行辩护,所引起的相同观察者,其变化也是不同的。
Observer:观察者抽象类,拥有的是一些共有等功能方法接口、数据,如状态更新等等
ConcreteObserver:具体类,去具体实现每个观察者状态变化时可能进行的数据变化、行为操作等等
使用场景:
- 一对多的关系网中,一个对象变化,随之变化的对象不知道具体多少个的时候
- 需要一种链式触发的时候。比如审判-报纸-政府反应等等
- 对象变化必须通知一些未知对象
- 当A依赖于B,并且需要分开来进行拓展的时候
优点:
- 耦合度降低
- 建立了一套触发机制,和反射相似。
缺点:
- 通知的时候,需要观察者队列进行通知,如果队列过长,可能导致时间上的误差
- 当观察者过多的时候,容易写着了环形的互相触发机制
- 不知道观察类怎么变化,只能知道它变化了
实操
思维:
- 观察者,在代码中是被调用者,所以选择先定义,而内容就是状态变化,然后在这个方法接口中调用自己的方法完成行为执行
- 先写抽象的Observer,然后去完成具体的类ConcreteObserver
- 完成CSomThing类,其中含有观察者列表、删除和添加接口、循环通知观察者接口、共同的方法接口
- 实现法官类,具体实现需要实现的方法,我这里是进行判决,然后进行循环通知观察者。其实也可以自定义循环逻辑,添加参数进行判断等等
// ObserverModel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
using namespace std;
//抽象观察者,观察者所具备的一些功能
class CNewsPerson
{
public:
virtual void updateNews() {}
};
//具体观察者,去实现功能,
//就像等待审判的人有记者是网络写手、纸质写手等等。
class CPaperPerson:public CNewsPerson
{
public:
void updateNews()
{
cout << "今天的判审结果是“无罪”,明天的头条有了。" << endl;
}
};
//一些具体的事情或者变化,比如审判长,下面应该有很多人记者、涉案人员、律师等等
class CSomeThing
{
private:
//人员集中
vector<CNewsPerson*> m_vecPerson;
public:
//添加和删除人员
void addNewsPerson(CNewsPerson* person)
{
m_vecPerson.push_back(person);
}
void deleteNewsPerson(CNewsPerson* person)
{
//m_vecPerson.remove(person);
//删除观察者
}
virtual void toDoSomething() {}
//循环遍历观察者
void toUpdate()
{
for (int i = 0; i < m_vecPerson.size(); ++i)
{
if (m_vecPerson[i])
m_vecPerson[i]->updateNews();
}
}
};
//审判长的审判定锤会让很多人的状态变化,去执行需要执行的操作
//如记者会快速写文章抓第一条信息等等
//而在实际中,可能会自己重写一些遍历或者添加一些条件来进行不同
//人员对同一个事务变化而会执行的不同操作
class CJudge :public CSomeThing
{
public:
void toDoSomething()
{
cout << "这个案子的判决是:无罪." << endl;
toUpdate();
}
};
int main()
{
CSomeThing *thing = new CJudge();
CNewsPerson *person = new CPaperPerson();
thing->addNewsPerson(person);
thing->toDoSomething();
}