天天看點

大話設計模式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.觀察者模式

觀察者模式所作的工作就是解耦合,讓耦合的雙方都依賴于抽象,進而使得各自的變化都不會影響另一邊變化。使用時機:當一個對象改變需要同時改變其他對象時②

繼續閱讀