{eyou:include file='banner.htm'/}
C++屌屌的觀(guān)察者模式(同步回調和異步回調)
2026-05-05 09:12:30
83
[摘要] 天津九安特機電工程有限公司(www.hunqingrc.com)C++屌屌的觀(guān)察者模式同步回調和異步回調)一、概述說(shuō)起觀(guān)察者模式,也是比較簡(jiǎn)單的一種模式了,稍微工作有1年經(jīng)驗的同學(xué),寫(xiě)起來(lái)都是666…想看觀(guān)察者模式的說(shuō)明可以直接上菜鳥(niǎo)教程|觀(guān)察者模式這

C++屌屌的屌屌的調和觀(guān)察者模式ヽ(′ー`)ノ(同步回調和異步回調)

一ヽ(′▽?zhuān)?ノ、概述

說(shuō)起觀(guān)察者模式,觀(guān)察也是式同比較簡(jiǎn)單的一種模式了,稍微工作有1年經(jīng)驗的異步同ヽ(′?`)ノ學(xué),寫(xiě)起來(lái)都是回調666̷(′;ω;`)0;

想看觀(guān)察者模式的說(shuō)明ヽ(′ー`)ノ可以直接上菜鳥(niǎo)教程|觀(guān)察者模式(shi)這個(gè)地址去看。

本篇文章其實(shí)就是屌屌的調和一個(gè)簡(jiǎn)單的觀(guān)察者模式,只是觀(guān)察使用了模板的方式,把我們的式同回調接口進(jìn)行了參數化,這樣有什么?好處呢?異步

好處當然是大大的有了。平時(shí)我們在不同業(yè)務(wù)邏輯之間寫(xiě)觀(guān)察者(zhe)模式呢,回調都得寫(xiě)好多個(gè),屌屌的調和大家??有沒(méi)有發(fā)現(xian),觀(guān)察所有???的式同被觀(guān)察者Subject其實(shí)很多操作都是(′?`*)一樣的。

本篇我們帶來(lái)兩種(zhong)觀(guān)察者模式:同步觀(guān)察者和異步觀(guān)察者

1、異步同步觀(guān)察者

如效果展示圖中的??第一個(gè)單次拉取頁(yè)簽,當我們點(diǎn)擊拉取按鈕時(shí),就相當于觸發(fā)了一次Subject對象的Update操作

2、異步觀(guān)察者

異步觀(guān)察者模式上和同步觀(guān)察者基本一樣,只是在事件處理上有稍微不同

執行Update操作是由Subject自己去完成的調用Observ(◎_◎;)er的OnUpdate回調接口時(shí),處于工作線(xiàn)程中Subject所有的請求操作都是在工作現場(chǎng)中進(jìn)行

如效果圖所示,定時(shí)拉取觀(guān)察者模式,Subject啟動(dòng)了一個(gè)后臺線(xiàn)程,3秒鐘拉取一次數據,并回調到界面

二、效果展示

如下圖所示,是一個(gè)簡(jiǎn)單的觀(guān)察者模式事例。

單次拉?。貉菔玖?′?`)同步觀(guān)察者模式

定時(shí)拉?。貉菔玖水惒接^(guān)察者模式

實(shí)現文件的目錄和頭文??件類(lèi)似,為了截圖方便所(′?_?`)以做了隱藏操作。

Header Files目錄下有2個(gè)虛擬文件(???)夾,分別就是對單次拉取和定時(shí)拉取??功能的實(shí)踐

下面我們就來(lái)正式開(kāi)??始講解這個(gè)屌屌的觀(guān)(guan)察者模式

三、(′?_?`)同步觀(guān)察者

1、首先就是定義一堆接口和回調參數

struct DataItem{ std::string strID; std::string strName; };typedef IUpdate1<DataItem&??gt; ISignalObserver;??//單次回調stヽ(′ー`)ノruct ISignal : public SubjectBase<ISignalObserver>{ virtual voi(′?ω?`)d RequestData() = 0;};

2、業(yè)務(wù)觀(guān)察者

這里我定義了一個(gè)SignalResponse業(yè)務(wù)觀(guān)察者,也就是我們在開(kāi)發(fā)工程中的實(shí)際功能類(lèi)。

class SignalResponse : public ISignal{ public: SignalResponse(); ~SignalResponse();public: virtual void RequestData() override;private: };

*3、獲取觀(guān)察者指針

**

通過(guò)一個(gè)門(mén)面接口獲取觀(guān)察者指針

調用ISignal的Attach接口,就可以把自己添加到觀(guān)察(′?`*)者列表。調用ISignal的RequestData接口,就可以拉取數據。調用ISignal的Detach接口,就可以把自己從觀(guān)察者列表中(′▽?zhuān)?移除。ISignal * GetSignalCommon();

4、UI界面

接下來(lái)就是寫(xiě)一個(gè)UI界面啦,當我們通過(guò)上一步調用拉取數據接口后,我們的UI上相應的OnUpdate接口就會(huì )被回調

class SignalWidg(?_?;)et : public?? QWidget, publi(′▽?zhuān)?c ISignalObserver{ Q_OBJEC(′;д;`)Tpublic: SignalWidget(QWidget * parent = 0); ~Si??gnalWidget();protected: virtual void OnUp??date(const DataItem &) over??ride;(╯‵□′)╯private slots: void on_pushButton_clicked();private: Ui::SignalWidget *ui;};

通過(guò)以上四步,就可以很方便的實(shí)現一個(gè)現在業(yè)務(wù)中的觀(guān)?察者,是不是很簡(jiǎn)單呢,編寫(xiě)過(guò)程中,需要完成這幾個(gè)地方

需(???)要定義我們回調函數的參數結構需要實(shí)例化一個(gè)被觀(guān)察者ヾ(′▽?zhuān)??接口類(lèi)實(shí)例化一個(gè)業(yè)務(wù)觀(guān)察者做一個(gè)UI界面,并集成第ヽ(′▽?zhuān)?/二步實(shí)例化的被觀(guān)察者的模板參數(接口類(lèi))

注意看這里的ISignalObserver,是不是很眼熟,其實(shí)他就是我們的模板被觀(guān)察者SubjectBase的模板參數。

講到這里,大家是不是都很關(guān)心這個(gè)模板觀(guān)察者到底是何方神圣,居然這么叼。那么接下來(lái)就是模板SubjectBase出場(chǎng)啦。。??。

下面我直( ?ω?)接給出代碼,學(xué)過(guò)C++的同學(xué)閱讀起來(lái)應該都不難。

覺(jué)著(zhù)難了就多讀幾遍

template <t??ypename T>struct ISu??bject{ virtual void Attach(T * pObserver) = 0;?? virtual(′▽?zhuān)?) void Deta??ch(T * pObserver) = 0;};template <typename P>struct IUpdate1{ virtual void OnUpdate(con(′?`)st P& data) = 0;};template <typename P1, typename P2>struct IUpdate2{ virtual void OnUpdate2(const P1 & p1, const P2 & p2) = 0;};template <typename P&ˉ\_(ツ)_/ˉgt;stru(??-)?ct IUpdate1_P{ virtual void OnUpdate(const P * da??ta) = 0;};template <typename T>struct SubjectBa??se{ public: virtual void Attach(T * pObserver) { std::lock_guard<std::mutex> lg(m_mutex);#ifdef _DE??BUG if (m_observers??.end() != std::find(m_observers.begin(), m_observers???.end(), pObserver)) { assert(false); }#endif // _DEBUG m_observers.push_back(pObserver); } virtual void Detach(T * pObserver) { std::lock_guard<std::mutex> lg(m_mutex); auto it = std::find(m_observ(′▽?zhuān)?ers.begin(), m_ob??servers.end(), pObserver); if (it != m_obser(′▽?zhuān)?vers.end()) { m_observers.erase(it); } else { assert(false); } } //protected: template <typename P> void UpdateImpl(c??onst P & data) { std::lock_g(′ω`)uard<mu(′?`)tex> lg(m_mutex); for (T * observer : m_observers) { observer->OnUpdate(ヾ(′?`)?data); } } template <typename P> void UpdateImpl(P & dat(′ω`)a) { std::lock_guard<std::mutex> lg(m_mutex); for (T* observer : m_observers) { observer->OnUpdate(data); } } template &l(╥_╥)t;t┐(′ー`)┌ypename P1, typename P2> void UpdateImpl(const P1& p1, const?? P2& p2) { std::lock_guard<mutex> lg(m(′?_?`)_mutex); for (T* observer?? : m_observers) { observer->OnUpdate2(p1, p2); } } template <typename P1, typename P2> voi(′?`*)d UpdateImpl(P1& p1, P2& p2) { std::lock_guard<mutex> lg(m_mutex); for (T* observer : m_observeヽ(′▽?zhuān)?ノrs) { observer->OnUpdate2(p1, p2); } } template <typename P> void UpdateImpl(const P * data)?? { std::lock_guard<mutex> lg(m_mutex); for (T * observer : m_observers) { observer(′▽?zhuān)?)->OnUpdate(data??); } } template <typename P&g??t(′?ω?`); void UpdateImpl(P * data) { std::l??ock_guard<mutex> lg(m_mutex)(???); for (T* observer : m_observers) { obserヽ(′▽?zhuān)?ノver->OnUpdate(data); } }protected: std??::m(′▽?zhuān)?utex m_mutex; std::list<T(╬?益?) *> m_observers;};

四、異步觀(guān)察者

異步觀(guān)察者(╯°□°)╯的實(shí)現和同步觀(guān)察者的結構基本一樣,都是使用(yong)同樣的套路,唯一有區別的地方就是,異步觀(guān)(????)察者所有的邏輯處理操作都是在工作線(xiàn)程中的。

由于IT??imerSubject和SubjectBase很??多接口都是一樣的,因此我這里就只把差異的部分貼出來(lái)(°o°)。

1、線(xiàn)程

ITimerSubject對象在構造時(shí),就啟動(dòng)了一( ?ω?)個(gè)線(xiàn)程,然后在(zai)線(xiàn)程中定時(shí)執行TimerNotify函數

ITimerSubject(){ m_thread = std::thread(std::bind(&ITimerSubject::TimerNotify, this));}vir(′▽?zhuān)?tual ~ITimer(╯°□°)╯︵ ┻━┻Subject(){ m_thread.join();}

再(?????)來(lái)看下定時(shí)處理任務(wù)這個(gè)函數,這個(gè)函數本身是用boost的庫實(shí)現(xian)我的,我改成C++11的模式的,新城退出這塊有些問(wèn)題,我沒(méi)有處理,這個(gè)也不是本篇文章的核心要講解的東西。

怎么優(yōu)雅的退出std::thread,這個(gè)從網(wǎng)上查下??資料吧,我能想到的也就是加一個(gè)標識,然后子??線(xiàn)程??去判斷。如果大家有更好??的辦法的話(huà)可以(yi)私信我,或者在底部留言。

void TimerNotify(){ for (;;) { //std::this_thread::interruption_point(); bool bNotify = false; { std::lock_gu??ard<std::mutex> lg(m_mutex); bNotify = m_sleepin??g_observers.size() < m_observers.size() ? true : false; } if (bNotify) { OnTimerNotify(); } //std::this_thread::interruption_point(); std::(′?ω?`)chrono::milliseconds timespan(GetTimerInterval() * 1000); // or whatever std::this_thread::sleep_for(timespan); }}

2、定義一??堆接口和回調參數

struct TimerDataItem??{ std::string strID; std::string strName;};typedef IUpdate1<TimerDataItem> ITimerObserver;//??定時(shí)回調struct ITimer : public ITimerSubject<ITimerObserver, std::string,?? TimerDataItem>{ };

3、業(yè)務(wù)觀(guān)察者

這里我定義了一個(gè)TimerResponse??業(yè)務(wù)觀(guān)察者,也就是我們在開(kāi)發(fā)工程中的實(shí)際功能類(lèi)。

class TimerResp??onse : public ITimer{ public: TimerR(╥_╥)esponse(); ~TimerRes??ponse();protected: virtual void OnNotify() override;private: };

TimerResponse::OnNotify()這個(gè)接口的實(shí)現就像這樣,這里需要注意的一點(diǎn)是,這個(gè)函數的執行位于工作線(xiàn)程中,也就意味著(zhù)UI界面的回┐(′ー`)┌調函數也在工作線(xiàn)程中,操作UI界面時(shí),一定??需要拋事件到UI??線(xiàn)程中。

void TimerResponse::OnNotify(){ static int id = 0; static std::string name = "miki"; id += 1; TimerDataIt??em item;?? std::stringstream ss; ss << "timer" &l(′▽?zhuān)?t;??< id; item.strID = ss.str(); item.strName = name; UpdateImpl??(item);}

OnNotify會(huì )定時(shí)被調用,然后去(qu)更(′ω`)新UI上的內容。

4ヽ(′ー`)ノ、獲取觀(guān)察者指針

通過(guò)一個(gè)門(mén)面接口獲取觀(guān)察者指針,調用ITimer的Attach接口把自己添加到觀(guān)察???者列表,然??后就可以定時(shí)獲取到數據,反之(zhi)也能把自己從觀(guān)察者列表中移除,并停止接收到數據。

ITimer * Get???TimerCommon();

5、UI界面

定時(shí)回調功能測試界面

on_pushButton_clicked槽函數只是(′?`)為了把當前線(xiàn)程喚醒,并定時(shí)回調OnUpdate屬于定時(shí)回調接口class TimerWidget : public QWidget, public ITimerObse??rver{ Q_OBJECTpublic: TimerWidget(QW??idget *parent = 0); ~Tim(╯‵□′)╯erWidget();protected: virtual void OnU??pdate(const TimerDat(′_`)aItem &) override;private slots: void on_pus(′-ι_-`)hBu??tton_clicked();signals: void RerfushData(Time??rDataItem);private: Ui::TimerWidget *ui;};

上邊也強調過(guò)了,On(′?`)Update的執行是在工作線(xiàn)程中的,因此實(shí)現的時(shí)候,如果涉及到訪(fǎng)問(wèn)UI界面,一定要注意切換線(xiàn)程

void TimerWidget::(′;д;`)OnUpdate(const TimerDataItem & item){ //注意這里的定時(shí)回調都在工作線(xiàn)程中 需要切換到主線(xiàn)程 emit RerfushData(item);}

以上講解就是我們觀(guān)察者的實(shí)現了,如果有疑問(wèn)歡迎提出


推薦閱讀

亚洲女同成aV人片在线观看|亚洲www啪成人一区二区麻豆|亚洲国产中日韩精品综合|亚洲国产成人精品一级片|亚洲无码在线视频免费

亚洲女同成aV人片在线观看|亚洲www啪成人一区二区麻豆|亚洲国产中日韩精品综合|亚洲国产成人精品一级片|亚洲无码在线视频免费 环江| 普安县| 勃利县| 东阳市| 房山区| 清镇市| 霍山县| 沈阳市| 保亭| 茂名市| 洛阳市| 方正县| 新营市| 蓬安县| 永和县| 平昌县| 西青区| 荆州市| 临漳县| 宝应县| 衡东县| 汉源县| 陕西省| 嫩江县| 蚌埠市| 商城县| 白水县| 久治县| 白沙| 平安县| 温泉县| 南宫市| 台北市| 西丰县| 肥西县| 涿鹿县| 白银市| 顺义区| 青海省| 电白县| 阿巴嘎旗| http://444 http://444 http://444 http://444 http://444 http://444