一、原理講解
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