C++設計模式——觀察者模式(observer pattern)

一、原理講解

1.1意圖

定義一種“一對多”的關係。當一個對象(被觀察者/發佈者)的狀態發生改變時,所有依賴它的對象都將得到通知並更新。又有別名爲發佈-訂閱(publish-subscribe)。

1.2應用場景

  • 一個抽象模型有兩個方面,其中一個方面依賴於另一方面。將這二者封裝在獨立的對象中,以使它們可以獨立的改變和複用
  • 當一個對象改變,連帶其它對象也跟着改變,但是不需要知道具體改變對象的數量

1.3代碼實現步驟

a1 先實現一個抽象主題接口類ISubject,主題接口提供增加、刪除、通知接口

a2 在實現一個抽象觀察者接口類IObserver,觀察者接口提供更新操作接口

a3 實現兩個具體觀察者類,繼承觀察者接口類,實現具體更新函數update()

a4 實現一個具體的主題類,繼承抽象接口類,實現具體的增加、刪除、通知觀察者函數細節。

二、實現代碼

#include <iostream>
#include <list>

using namespace std;

class IObserver;

//主題接口,抽象基類
class ISubject {	
public:
	virtual ~ISubject(){}

	virtual void add(IObserver *pObserver) = 0;
	virtual void remove(IObserver *pObserver) = 0;
	virtual void notify() = 0; //通知

protected:
	ISubject() {}
};

//觀察者接口,抽象基類
class IObserver
{
public:	
	virtual ~IObserver() {}

	virtual void update() = 0; //更新接口

protected:
	IObserver(){}
};

//具體觀察者,實現具體細節函數,比如更新函數update(){...}
class Observer1 : public IObserver
{
public:
	Observer1(){}
	~Observer1(){}

	void update() { //觀察者得到通知並執行更新函數
		cout << "run Observer1 function update()" << endl;
	}
};

//具體觀察者,實現具體細節函數,比如更新函數update(){...}
class Observer2 : public IObserver
{
public:
	Observer2() {}
	~Observer2() {}

	void update() { //觀察者得到通知並執行更新函數
		cout << "run Observer2 function update()" << endl;
	}
};

//核心操作在具體主題類這裏,實現增加、刪除、通知觀察者和更新
class Subject : public ISubject
{
public:
	Subject(){}
	~Subject(){}

	virtual void add(IObserver *pObserver) {
		observerList.push_back(pObserver);
	}

	virtual void remove(IObserver *pObserver) {
		observerList.remove(pObserver);
	}

	virtual void notify() { //實現細節,通知觀察者並更新
		for (std::list<IObserver*>::iterator iter = observerList.begin(); iter != observerList.end(); iter++) {
			(*iter)->update();
		}
	}

	void setState(int state) {
		m_state = state;		
	}

private:
	std::list<IObserver *> observerList;
	int m_state; //狀態值,如果發生改變,表示狀態改變了,需要通知觀察者並更新
};

int main()
{
	Subject *subject = new Subject();
	Observer1 *oberver1 = new Observer1();
	Observer2 *oberver2 = new Observer2();

	//調用流程:改變狀態->註冊觀察者1和觀察者2->通知觀察者
	subject->setState(1); //狀態第一次發生改變
	subject->add(oberver1); //添加/註冊觀察者1
	subject->add(oberver2); //添加觀察者2
	subject->notify();
	
	subject->setState(2); //狀態第二次發生改變
	subject->remove(oberver2); //刪除觀察者2
	subject->notify(); //發佈消息通知

	delete subject;
	delete oberver1;
	delete oberver2;

	getchar();
	return 1;
}

三、總結

3.1代碼實現解讀

步驟a4的本質在於主題接口類組合一系列觀察者類對象,也即是觀察者類對象列表。通過在主題類實現具體細節,進行觀察者的增加、刪除、通知更新(也就是遍歷觀察者列表對象並調用其更新函數)

步驟a4是類的組合和繼承的一種巧妙用法,繼承主題抽象接口類,組合抽象觀察者基類,在運行時動態調用觀察者update()函數,也就是將update()的實現細節放在了子類中實現!!!

3.2功能總結

抽象被觀察者(也稱爲目標)(subject)、具體被觀察者(也稱爲具體目標)(concreteSubject),抽象觀察者(object)、具體觀察者(concreteObserver)的功能總結如下。

3.2.1  subject(抽象目標,或者是抽象接口類)

  • 目標知道它的觀察者。可以 有任意多個觀察者觀察同一目標。
  • 提供註冊(增加)和刪除觀察者的接口

3.2.2  observer(抽象觀察者)

  • 定義一個更新接口,當目標對象改變時,就會通知到這個接口

3.2.3  concreteSubject(具體目標)

將相關狀態存入具體觀察者(concreteObserver)

當自身狀態發生改變時,通知每一個註冊了的觀察者

3.2.4  concreteObserver(具體觀察者)

  • 維護一個指向具體目標(concreteSubject)對象的應引用
  • 存儲有關狀態,這些狀態應與目標的狀態保持一致
  • 實現抽象觀察者(observer)的更新接口,以使自身狀態與目標狀態保持一致

3.3滿足設計模式八大設計原則

a1依賴導致原則(DIP):高層模塊不應該依賴於底層模塊(變化),二者都應該依賴與抽象(穩定);

a2開閉原則(OCP):對擴展開發(比如本示例中的不同算法),對更改封閉(比如本例中的run()函數);

a6優先使用類對象組合,而不是類繼承:子類對象只要求被組合對象具有良好定義的藉口,耦合度低;

a8針對接口編程,而不是針對實現編程:不將變量申明爲某個特定的類,而聲明爲某個接口。客戶無需知道具體類型,只需要知道特定接口。減少系統依賴關係,實現高內聚、松耦合。

 

 

參考內容:

https://www.cnblogs.com/carsonzhu/p/5770253.html(參考:觀察者模式(重點參考原理和代碼!!!))

https://blog.csdn.net/zhouzhenhe2008/article/details/74784277(參考:觀察者模式(參考原理講解))

https://blog.csdn.net/qq_36391130/article/details/82939799(參考:設計模式八大設計原則)

https://blog.csdn.net/i_chaoren/article/details/80561204(參考:觀察者模式)

https://www.jianshu.com/p/1b1def6960a4(參考:觀察者模式)

https://www.bilibili.com/video/av22292899?from=search&seid=8813426322713310552(參考:嗶哩嗶哩C++設計模式!!!)

Erich Gamma,Richard Helm.《設計模式 可複用面向對象軟件的基礎》[M].機械工業出版社,2019:219-227

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章