天天看点

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

大话设计模式9—观察者模式(老板回来,我不知道)

  • 1.需求:老板回来,我不知道
  • 2.双向耦合的设计
    • 2.1 前台秘书类
    • 2.2 看股票同事类
    • 2.3main函数及输出
  • 3.解耦修改
    • 3.1 抽象观察者类
    • 3.2 前台秘书类
    • 3.3 main函数
  • 4.观察者模式实现
    • 4.1 通知者
    • 4.2 观察者
    • 4.3 main函数
  • 5.观察者模式

1.需求:老板回来,我不知道

假设同事1、同事2上班摸鱼炒股,为了防止老板发现,如果前台发觉老板回来迅速打电话告知两位同事。

2.双向耦合的设计

根据需求,可以设计两个类前台秘书类

class Secretary

和看股票同事类

class StockObserver

2.1 前台秘书类

前台秘书类

class Secretary

的设计如下:有两个数据成员分别是存储着看股票同事类指针的数组,用于记录哪些同事需要前台帮忙观察老板是否回来;还需要一个字符串存储当前台发现老板回来打电话所说的内容。

那么前台秘书类的功能如下:①添加操作:添加需要帮忙的同事。②通知操作:通知数组中的同事。③设置和读取打电话的内容。

//Secretary.h
#ifndef SECRETARY_H
#define SECRETARY_H
#include<vector>
#include"StockObserver.h"
//前台秘书类
class Secretary {
private:
	vector<StockObserver*> m_observer;
	string m_action;
public:
	//Secretary();
	void Attach(StockObserver* observer);
	void Notify();
	void SetSecretaryAction(string action);
	string GetSecretaryAction();
};
#endif 
           

具体实现也比较简单,①添加操作:添加到vector。②通知操作:遍历数组,调用炒股同事的update方法(后续再提)。③设置和读取打电话的内容,也就是Set和Get m_action内容即可。

#include"Secretary.h"
#include"StockObserver.h"

//Secretary::Secretary() {
//	m_action = "老板没在";
//}

void Secretary::Attach(StockObserver* observer)
{
	m_observer.push_back(observer);
}

void Secretary::Notify()
{
	for (auto ob : m_observer)
	{
		ob->update();
	}
}
void Secretary::SetSecretaryAction(string action)
{
	m_action = action;
}

string Secretary::GetSecretaryAction()
{
	return m_action;
}
           

2.2 看股票同事类

看股票同事类

class StockObserver

的设计思路如下,需要有看股票同事的名字、前台秘书指针。构造函数负责初始化姓名和绑定前台秘书,上节中的update方法用来表示收到前台通知时需要采取的行动。

注意下这两个类属于互相引用,需要前置声明class Secretary;并在.cpp中实现其方法。

//StockObserver.h
#ifndef STOCKOBSERVER_H
#define STOCKOBSERVER_H
#include<string>
#include<iostream>
#include"Secretary.h"
using namespace std;
class Secretary;

//看股票同事类
class StockObserver {
private:
	string m_name;
	Secretary *m_sub;
public:
	StockObserver(string name, Secretary *sub);
	void update();
};
#endif // !STOCKOBSERVER_H

           

具体实现如下:update方法通过前台秘书的指针获取到前台电话内容,并执行关掉股票行情,继续工作。

#include"Secretary.h"
#include"StockObserver.h"
StockObserver::StockObserver(string name, Secretary *sub) :m_name(name), m_sub(sub){
}
void StockObserver::update()
{
	cout << m_sub->GetSecretaryAction()<<" "<<m_name ;
	cout << "关掉股票行情,继续工作" << endl;
}
           

2.3main函数及输出

最后main函数实现如下,在秘书类的对象中添加需要帮忙的同事1和同事2。当老板回来时将老板已回信息传递通知出去。

#include"Secretary.h"
#include"StockObserver.h"

int main()
{
	Secretary* qiantai = new Secretary();
	StockObserver *tongshi1 = new StockObserver("同事1", qiantai);
	StockObserver *tongshi2 = new StockObserver("同事2", qiantai);
	
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->SetSecretaryAction("老板已回");
	qiantai->Notify();
	return 0;
}
           

实现题设要求的内容。

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

3.解耦修改

分析上述实现,可以发现类的设计不够聪明,前台秘书类里面有装有炒股同事(观察者)的数组,而炒股同事又有前台秘书类的指针来获取前台的状态,属于双向耦合。此外,如果还有同事需要帮忙,但是并不是炒股,而是看NBA,那么不能只执行关掉股票,需要个性化定制update方法,故可以抽象出来观察者,不同的同事作为具体的观察者继承实现。此外,还可以让前台去除帮忙的同事。

3.1 抽象观察者类

依据分析需要定制化update方法,我们可以设计一个抽象Observer类,而将看股票的同事和看NBA 的同事作为其子类重写update方法。

//Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H
#include<string>
#include<iostream>
#include"Secretary.h"
using namespace std;
class Secretary;
class Observer {
protected:
	string m_name;
	Secretary* m_sub;
public:
	Observer(string name, Secretary* sub);
	virtual void Update();
};

class StockObserver:public Observer{
public:
	StockObserver(string name, Secretary* sub);
	void Update();
};

class NBAObserver :public Observer {
public:
	NBAObserver(string name, Secretary* sub);
	void Update();
};
#endif
           

具体实现除了为不同的同事输出不同的执行动作外和上节相同。

//Observer.cpp
#include"Observer.h"
Observer::Observer(string name, Secretary* sub):m_name(name),m_sub(sub) {
}

StockObserver::StockObserver(string name, Secretary* sub) :Observer(name, sub) {
}
void StockObserver::Update() {
	cout << m_sub->GetSecretaryAction() << m_name;
	cout <<"关掉股票情况,继续工作" << endl;
}

NBAObserver::NBAObserver(string name, Secretary* sub) :Observer(name, sub) {
}
void NBAObserver::Update() {
	cout << m_sub->GetSecretaryAction() << m_name;
	cout << "关掉NBA比赛,继续工作" << endl;
}
           

3.2 前台秘书类

前台秘书类大体内容不变,只是增加一个删除功能,即某同事不需要帮忙,需要从数组中删除。

//Secretary.h
#ifndef SECRETARY_H
#define SECRETARY_H
#include<string>
#include<vector>
#include"Observer.h"
using namespace std;
class Observer;

//前台秘书类
class Secretary {
private:
	string m_action;
	vector<Observer*> m_observers;
public:
	void Attach(Observer* ob);
	void Detach(Observer* ob);
	void Notify();
	void SetSecretaryAction(string action);
	string GetSecretaryAction();
};
#endif 
           

实现如下,其中增加Attach、删除Detach针对抽象Observer类编程,减少了与具体类的耦合。

//Secretary.cpp
#include"Secretary.h"
void Secretary::Attach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos == m_observers.end())
	{
		m_observers.push_back(ob);
	}
}
void Secretary::Detach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos != m_observers.end())
	{
		m_observers.erase(pos);
	}
}
void Secretary::Notify() {
	for (auto ob : m_observers)
		ob->Update();
}
void Secretary::SetSecretaryAction(string action) {
	m_action = action;

}
string Secretary::GetSecretaryAction() {
	return m_action;
}
           

3.3 main函数

main函数中大体不变,先通知三个同事,再去掉一个通知。

#include"Observer.h"
#include"Secretary.h"
int main()
{
	Secretary* qiantai = new Secretary();
	StockObserver *tongshi1 = new StockObserver("同事1", qiantai);
	NBAObserver *tongshi2 = new NBAObserver("同事2", qiantai);
	NBAObserver *tongshi3 = new NBAObserver("同事3", qiantai);
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	qiantai->SetSecretaryAction("老板回来啦!!!");
	qiantai->Notify();
	cout << "........." << endl;
	qiantai->Detach(tongshi2);
	qiantai->Notify();
	return 0;
}
           

初步解耦,并完成其中的功能。

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

4.观察者模式实现

分析初步解耦代码的缺点:对于上述通知者来说,应该可以有多个通知者存在,试想如果前台没来得及通知观察者老板回来了,那么发出通知的可能是老板,也可能是其他人而且发出通知的内容也不一样。所以可以抽象出来一个通知者,而老板、前台都是继承他的具体通知者,这样观察者就可以不依赖具体实现,达到解耦。

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

此外,如下图在实例化观察者同事1、同事2 的时候,构造函数直接绑定了前台作为其通知者,所以,我们考虑在构造函数中暂不绑定谁作为通知者,新增设置通知者SetNotifier()方法来绑定通知者。

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

4.1 通知者

抽象通知者和具体通知者实际并没有任何区别,其增加、删除观察者,通知等方法完全相同,只是具体通知者需要通知的对象数组可能不同,所以,抽象观察者和上节前台秘书类相同,具体通知者boss和Secretary 继承就可以。

//Notifier.h
#ifndef NOTIFIER_H
#define NOTIFIER_H
#include<string>
#include<vector>
#include"Observer.h"
using namespace std;
class Observer;

//抽象通知者
class Notifier {
protected:
	string m_action;
	vector<Observer*> m_observers;
public:
	void Attach(Observer* ob);
	void Detach(Observer* ob);
	void Notify() ;
	void SetNotifierAction(string action);
	string GetNotifierAction();
	virtual ~Notifier() {};
};

class Boss :public Notifier {
};

class Secretary :public Notifier {
};
#endif 
           
//Notifier.cpp
#include"Notifier.h"
void Notifier::Attach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos == m_observers.end())
	{
		m_observers.push_back(ob);
	}
}
void Notifier::Detach(Observer* ob) {
	auto pos = find(m_observers.begin(), m_observers.end(), ob);
	if (pos != m_observers.end())
	{
		m_observers.erase(pos);
	}
}
void Notifier::Notify() {
	for (auto ob : m_observers)
		ob->Update();
}
void Notifier::SetNotifierAction(string action) {
	m_action = action;

}
string Notifier::GetNotifierAction() {
	return m_action;
}
           

4.2 观察者

观察者上一节已经解耦了,抽象出来一个观察者,让看NBA的和炒股的同事继承它,本节沿用上一节内容,只是将构造函数只初始化姓名。

//Observer.h
#ifndef OBSERVER_H
#define OBSERVER_H
#include<string>
#include<iostream>
#include"Notifier.h"
using namespace std;
class Notifier;
class Observer {
protected:
	string m_name;
	Notifier* m_sub;
public:
	Observer(string name);
	virtual void Update()=0;
	void SetNotifier(Notifier* sub);
};

class StockObserver:public Observer{
public:
	StockObserver(string name);
	void Update();
};

class NBAObserver :public Observer {
public:
	NBAObserver(string name);
	void Update();
};
#endif
           
//Observer.cpp
#include"Observer.h"

Observer::Observer(string name):m_name(name) {
}

void Observer::Update() {
}

void Observer::SetNotifier(Notifier* sub) {
	m_sub = sub;
}

StockObserver::StockObserver(string name) :Observer(name) {
}
void StockObserver::Update() {
	cout << m_sub->GetNotifierAction() << m_name;
	cout <<"关掉股票情况,继续工作" << endl;
}

NBAObserver::NBAObserver(string name) :Observer(name) {
}
void NBAObserver::Update() {
	cout << m_sub->GetNotifierAction() << m_name;
	cout << "关掉NBA比赛,继续工作" << endl;
}
           

4.3 main函数

#include"Observer.h"
#include"Notifier.h"
int main()
{
	//实例化第一个通知者前台
	Secretary* qiantai = new Secretary();
	//实例化三个观察者,两个看NBA、一个炒股
	StockObserver*tongshi1 = new StockObserver("同事1");
	NBAObserver*tongshi2 = new NBAObserver("同事2");
	NBAObserver*tongshi3 = new NBAObserver("同事3");
	//通知者将需要帮忙的同事添加到数组中
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	//观察者也绑定前台
	tongshi1->SetNotifier(qiantai);
	tongshi2->SetNotifier(qiantai);
	tongshi3->SetNotifier(qiantai);
	//通知者打电话,告诉老板回来了
	qiantai->SetNotifierAction("咱老板回来啦!!!");
	qiantai->Notify();
	//通知者去掉同事2再通知老板回来了
	qiantai->Detach(tongshi2);
	qiantai->Notify();
	
	return 0;
           
大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

再看看如果老板通知员工是怎么样的,需要绑定老板

#include"Observer.h"
#include"Notifier.h"
int main()
{
	//实例化第一个通知者前台
	Secretary* qiantai = new Secretary();
	//实例化三个观察者,两个看NBA、一个炒股
	StockObserver *tongshi1 = new StockObserver("同事1");
	NBAObserver *tongshi2 = new NBAObserver("同事2");
	NBAObserver *tongshi3 = new NBAObserver("同事3");
	//通知者将需要帮忙的同事添加到数组中
	qiantai->Attach(tongshi1);
	qiantai->Attach(tongshi2);
	qiantai->Attach(tongshi3);
	//观察者也绑定前台
	tongshi1->SetNotifier(qiantai);
	tongshi2->SetNotifier(qiantai);
	tongshi3->SetNotifier(qiantai);
	//通知者打电话,告诉老板回来了
	qiantai->SetNotifierAction("咱老板回来啦!!!");
	qiantai->Notify();
	//通知者去掉同事2再通知老板回来了
	qiantai->Detach(tongshi2);
	qiantai->Notify();


	//老板通知
	cout << "........" << endl;
	Boss* boss = new Boss();
	boss->Attach(tongshi1);
	boss->Attach(tongshi2);
	boss->Attach(tongshi3);
	tongshi1->SetNotifier(boss);
	tongshi2->SetNotifier(boss);
	tongshi3->SetNotifier(boss);

	boss->SetNotifierAction("让俺看看谁没工作!!!");
	boss->Notify();
	boss->Detach(tongshi1);
	boss->Notify();

	return 0;
}
           
大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

完成了题设的要求。

5.观察者模式

观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,当这个主题对象状态发生改变时会通知所有观察者对象,使他们能够自动更新自己。

如下图所示,

Subject类是抽象通知者,将所有观察者的引用保存在数组中,并提供删除增加观察者对象。

Oberserver类是抽象观察者,在得到主题通知时更新自己,接口中有update方法交给子类实现

ConcreteSubject是具体通知者

ConcreteOberserver是具体观察者,保存一个指向具体主题对象的引用,实现抽象观察者要求的更新接口。

大话设计模式9—观察者模式(通知者与观察者)1.需求:老板回来,我不知道2.双向耦合的设计3.解耦修改4.观察者模式实现5.观察者模式

观察者模式所作的工作就是解耦合,让耦合的双方都依赖于抽象,从而使得各自的变化都不会影响另一边变化。使用时机:当一个对象改变需要同时改变其他对象时②

继续阅读