青島百度seoseo網(wǎng)站地圖
觀察者模式:
定義對象間的一種一對多(變化)的依賴關(guān)系,以便當(dāng)一個(gè) 對象(Subject)的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對象都 得到通知并自動(dòng)更新
動(dòng)機(jī):
在軟件構(gòu)建過程中,我們需要為某些對象建立一種“通知依賴關(guān)系”——一個(gè)對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關(guān)系過于緊密, 將使軟件不能很好地抵御變化。
使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化,并形成一種穩(wěn)定的依賴關(guān)系。從而實(shí)現(xiàn)軟件體系結(jié)構(gòu)的松耦合
總結(jié):
- 使用面向?qū)ο蟮某橄?#xff0c;Observer模式使得我們可以獨(dú)立地改變目標(biāo)與觀察者,從而使二者之間的依賴關(guān)系達(dá)致松耦合。
- 目標(biāo)發(fā)送通知時(shí),無需指定觀察者,通知(可以攜帶通知信息作為參數(shù))會自動(dòng)傳播。
- 觀察者自己決定是否需要訂閱通知,目標(biāo)對象對此一無所知。
- Observer模式是基于事件的UI框架中非常常用的設(shè)計(jì)模式,也是 MVC模式的一個(gè)重要組成部分。
實(shí)踐案例
假如現(xiàn)有有一個(gè)業(yè)務(wù)場景,我們需要寫一個(gè)視頻檢測器,該檢測器會使用圖像分割模型對輸入的視頻流進(jìn)行檢測,如果監(jiān)測到畫面有人,那么需要做針對人的具體操作(比如將人框出來,比如將人截取出來),如果檢測到畫面中有汽車,也會做具體操作(比如將汽車涂為紅色)等等。
這樣一個(gè)業(yè)務(wù)場景普通的寫法,我們很容易想到,讀取視頻流,然后對每一幀圖像檢測,然后寫if else
if 檢測到人 {…}
else if 檢測到汽車 {…}
else if 檢測到天空 {…}
這樣的實(shí)現(xiàn)方式可以滿足需求,但是并不滿足我們的設(shè)計(jì)原則
這是一個(gè)緊耦合的做法,你的檢測器要依賴于其他的視頻操作類,不符合我們的依賴倒置原則
我們可以將操作類抽象成一個(gè)接口,然后再需要操作的時(shí)候,調(diào)用接口。這樣就解決了這個(gè)問題
不過還有一點(diǎn),在這個(gè)業(yè)務(wù)場景中,操作類有不同的操作,也就是說需要多個(gè)操作
因?yàn)槲覀冞M(jìn)一步抽象
我們寫一個(gè)操作基類,然后再寫多個(gè)操作類1,操作類2,都繼承基類
到這一步,其實(shí)觀察者模型就出來了
觀察者模型:
我們可以把視頻看作一個(gè)被觀察者,檢測到的結(jié)果(人、汽車、樹木等),這些相當(dāng)于信息通知,給誰通知呢?給那些具體的操作類通知,所以我們可以把對人操作的類、對汽車操作的類這些看作觀察者。
被觀察者將消息發(fā)送給觀察者,觀察者根據(jù)消息來做不同的操作(多態(tài))
并且在這個(gè)過程中,支持觀察者自主選擇是否訂閱消息。
代碼實(shí)現(xiàn)以及注釋:
#include <string>
#include <iostream>
#include <list>
using namespace std;class Observer {// 抽象類(接口)
public:virtual void handleVideo(string detectInfo) = 0;virtual ~Observer() {}
};class Observer1 : public Observer {
public:virtual void handleVideo(string detectInfo) {cout << "截取人" << endl;}
};class Observer2 : public Observer {
public:virtual void handleVideo(string detectInfo) {cout << "截取汽車" << endl;}
};class VideoDetecter {string m_filePath;string m_fileName;list<Observer*> m_observerList; // 抽象通知機(jī)制,支持多個(gè)觀察者public:VideoDetecter(string filePath, string fileName) {m_filePath = filePath;m_fileName = fileName;}void detect() {//1.讀取視頻流cout << "讀取視頻流:" << m_filePath + m_fileName << endl;//2.循環(huán)每一幀處理int frameNum = 10;for (int i = 0; i < frameNum; i++) {//假設(shè)對第i幀圖像處理得到識別結(jié)果 結(jié)果記作 detectInfostring detectInfo = "識別結(jié)果";sendNotify(detectInfo);//發(fā)送通知}}void addObserver(Observer* observer) { //添加觀察者m_observerList.push_back(observer);}void removeObserver(Observer* observer) { //移除觀察者m_observerList.remove(observer);}
protected:virtual void sendNotify(string detectInfo) {list<Observer*>::iterator itor = m_observerList.begin();while (itor != m_observerList.end()) {(*itor)->handleVideo(detectInfo); //不同觀察者對通知做出響應(yīng)itor++;}}
};int main() {string filePath = "/root/home/videoPath/";string fileName = "001.mp4";Observer* observer;VideoDetecter detecter(filePath, fileName);Observer1 ob1;Observer2 ob2;detecter.addObserver(&ob1);detecter.addObserver(&ob2);detecter.detect();detecter.removeObserver(&ob1);detecter.removeObserver(&ob2);//detecter.detect();}