目錄
定義
結構
優點
缺點
适用場景
定義
觀察者模式(又被稱為釋出-訂閱(Publish/Subscribe)模式,屬于行為型模式的一種,它定義了一種一對多的依賴關系,讓多個觀察者對象對象同時監視某一個主題對象。這個主題對象的狀态變化時,會通知所有的觀察者對象,使他們能夠自動更新自己的狀态。
- 舉個例子:觀察者模式可以了解為, 在一個一對多的關系模式中, 例如一個微信公衆号有多個關注使用者,那麼關注該微信公衆号的微信使用者就是觀察者,微信公衆号就是被觀察者.
- 一個微信公衆号會有多個關注使用者,這就是其中的一對多的關系.然後當一個對象的狀态發生改變就是說當被觀察者(微信公衆号)有了改變,例如添加了新的内容.這時候所有關注該公衆号的微信使用者就希望能得到通知.
結構
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL4VFVOJTTU5EMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5ADNyITN1YTM0EDNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
觀察者模式UML圖
- Subject:抽象主題(抽象被觀察者),抽象主題角色把所有觀察者對象儲存在一個集合裡,每個主題都可以有任意數量的觀察者,抽象主題提供一個接口,可以增加和删除觀察者對象。
- ConcreteSubject:具體主題(具體被觀察者),該角色将有關狀态存入具體觀察者對象,在具體主題的内部狀态發生改變時,給所有注冊過的觀察者發送通知。
- Observer:抽象觀察者,是觀察者者的抽象類,它定義了一個更新接口,使得在得到主題更改通知時更新自己。
- ConcrereObserver:具體觀察者,實作抽象觀察者定義的更新接口,以便在得到主題更改通知時更新自身的狀态。具體觀察者角色可以儲存一個指向具體主題對象的引用。
下面是用C++ 代碼 實作大話資料結構本章:
#include<iostream>
#include<list>
#include<algorithm>
#include<string>
using namespace std;
class Observer // 抽象觀察者,為所有的觀察者定義更新接口 (Update),當收到 Subject 的通知時,Observer 需要同步更新資訊。
{
public:
virtual ~Observer() = default;
virtual void Update() = 0;
};
class Subject // 抽象主題類,也是抽象被觀察者
{
public:
virtual ~Subject() = default;
virtual void Attach(Observer* observer) = 0;
virtual void Detach(Observer* observer) = 0;
virtual void Notify() = 0; //通知所有觀察者
};
class ConcreteSubject : public Subject // 具體主題類,也是具體被觀察者
{
private:
string subjectState;
list<Observer*> observers;
public:
void Attach(Observer* observer)override //添加觀察者
{
observers.push_back(observer);
}
void Detach(Observer* observer)override // 删除觀察者
{
while (!observers.empty())
{
for (auto it = observers.begin(); it != observers.end(); ++it)
{
if (*it == observer)
{
observers.erase(it);
break;
}
}
break;
}
}
void Notify() override //通知所有觀察者
{
while (!observers.empty())
{
for (auto item : observers)
{
item->Update(); // 更新目前具體主題對象的狀态到所有觀察者中
}
break;
}
}
string GetSubjectState() //設定狀态
{
return subjectState;
}
void SetSubjectState(string state) // 獲得狀态
{
subjectState = state;
}
};
class ConcreterObserver : public Observer
{// 具體觀察者
private:
ConcreteSubject *m_subject;
string m_name;
public:
ConcreterObserver() = default;
ConcreterObserver(ConcreteSubject *subject, string name) : m_subject(subject),m_name(name) {}
void Update()override
{
// 具體主題對象狀态發生改變時,觀察者也發生改變。
auto observerState = m_subject->GetSubjectState();
cout << observerState << "," << m_name << "關閉股票行情,繼續工作!" << endl;
}
};
int main()
{
ConcreteSubject *huhansan = new ConcreteSubject;
ConcreterObserver *tongshi1 = new ConcreterObserver(huhansan, "魏關姹");
ConcreterObserver *tongshi2 = new ConcreterObserver(huhansan, "易管察");
ConcreterObserver *tongshi3 = new ConcreterObserver(huhansan, "霍華德");
huhansan->Attach(tongshi1);
huhansan->Attach(tongshi2);
huhansan->Attach(tongshi3);
//魏關姹沒有被老闆通知到,減去。
huhansan->Detach(tongshi1);
huhansan->SetSubjectState("我胡漢三回來了!");
huhansan->Notify();
delete huhansan;
delete tongshi1;
delete tongshi2;
delete tongshi3;
huhansan = nullptr;
tongshi1 = tongshi2 = tongshi3 = nullptr;
system("pause");
return 0;
}
運作結果截圖:
觀察者模式主要的作用就是對象解耦,将觀察者和被觀察者完全隔離,隻依賴于
Subject
和
Observer
,使得原本對象處于松耦合,不會互相影響。
優點
觀察者模式解除了具體主題和具體觀察者的耦合觀察者和被觀察者之間是抽象耦合的,容易擴充
- 由于主題接口僅僅依賴于觀察者接口,是以具體主題隻是知道它的觀察者是實作觀察者接口的某個類的執行個體,但不需要知道具體是哪個類。同樣,由于觀察者僅僅依賴于主題接口,是以具體觀察者隻是知道它依賴的主題是實作主題接口的某個類的執行個體,但不需要知道具體是哪個類。
- 觀察者模式滿足“開-閉原則”。主題接口僅僅依賴于觀察者接口,這樣,就可以讓建立具體主題的類也僅僅是依賴于觀察者接口,是以,如果增加新的實作觀察者接口的類,不必修改建立具體主題的類的代碼。同樣,建立具體觀察者的類僅僅依賴于主題接口,如果增加新的實作主題接口的類,也不必修改建立具體觀察者類的代碼。
缺點
- 抽象通知者依然依賴于抽象觀察者, 并未完全解除耦合
- 在應用觀察者模式時需要考慮一下開發效率和運作效率的問題,程式中包括一個被觀察者、多個觀察者,開發、調試等内容會比較複雜,而且消息的通知一般是順序執行,那麼一個觀察者卡頓,後面的所有觀察者都會停止。會影響整體的執行效率,在這種情況下,可以考慮使用異步( 指的是多線程)解決,同時也需要確定異步不會出現問題
适用場景
總的來講,觀察者模式所做的工作其實就是在解除耦合,讓耦合的雙方都依賴于抽象,而不是依賴于具體,進而使得各自的變化都不會影響另一邊的變化。
- 當一個對象的資料更新時需要通知其他對象,但這個對象又不希望和被通知的那些對象形成緊耦合。
- 當一個對象的資料更新時,這個對象需要讓其他對象也各自更新自己的資料,但這個對象不知道具體有多少對象需要更新資料。
- 關聯行為場景。關聯行為是可拆分的,而不是組合關系
- 事件多級觸發場景
- 跨系統的消息交換場景,如消息隊列的處理機制