C++實現觀察者模式(Linux環境)

什麼是觀察者模式

觀察者模式(有時又被稱爲模型-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟件設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。
觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。舉個例子,用戶界面可以作爲一個觀察者,業務數據是被觀察者,用戶界面觀察業務數據的變化,發現數據變化後,就顯示在界面上。面向對象設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個對象只做一件事情,並且將他做好。觀察者模式在模塊之間劃定了清晰的界限,提高了應用程序的可維護性和重用性。
觀察者設計模式定義了對象間的一種一對多的組合關係,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並自動刷新。(摘自百度百科)

有趣段子

首先我們來舉個栗子,對觀察者和被觀察者分配個人物角色
我們還是舉<<大話設計模式>>裏面的栗子(改編了一下):
有一個公司名叫X集團,裏面有若干個員工(觀察者),但是呢大部分員工懶的跟豬一樣,特別是男員工,看到老闆不在的時候還喜歡到網上亂看一些亂七八糟的視頻(世界人民都懂的),但是呢他們又非常膽小,生怕老闆突然回來看到他們這種種的齷齪行爲(主要還是怕被抄魷魚),但好在公司裏面有個美麗大方,善解人意的前臺小姐姐(被觀察者),每當前臺小姐姐看到老闆來了,就會用各種方式來通知公司裏的員工(打電話呀、微信呀、只有你想不到沒有小姐姐做不到的),以至於老闆一直都還矇在鼓裏,到現在還覺得他的員工積極向上,這麼傻的老闆難怪公司一直虧本 。
其實這就是觀察者模式,前臺小姐姐以廣播的形式通知公司裏的所有員工,當然小姐姐必須要有被通知的員工的聯繫方式。

代碼解析

下面我們開始來解析觀察者模式:
所謂觀察者模型那我們肯定少不了被觀察者

考慮到代碼的重用及擴展性等等等,我們將被觀察者進行接口的封裝,因爲代碼我大部分都寫了註釋所以我就不再做詳細的解釋了

抽象觀察者:“ObServer.h”

#ifndef __OBSERVER_H__
#define __OBSERVER_H__

#include <iostream>

/*
 * 抽象觀察者
 * 爲所有的具體觀察者定義一個接口,在得到主題的通知時更新自己
 */
class CObserver
{
public:
    virtual ~CObserver() {};

    // 用以更新當前類的抽象函數
    virtual void Update(const std::string &msg) = 0;
    
    virtual std::string getName() = 0;

};

#endif

具體觀察者:“ConcreteObserver.h”

#ifndef __CONCRETEOBSERVER_H__
#define __CONCRETEOBSERVER_H__

#include "Observer.h"
#include <string>

/*
 * 具體觀察者
 * 實現抽象觀察者角色所要求的更新接口
 * 以便使本身的狀態與主題的狀態相協調
 */
class ConcreteObserver : public CObserver
{
public:
    ConcreteObserver(std::string name) {
        name_ = name;
    }

    void Update(const std::string& msg) {
        if ("老闆來了" == msg) {
            // 做響應的操作
            std::cout << "老闆是爹!我" << name_ << "乖乖工作!保證不再看不良視頻!" << std::endl;
        }
        else {
            // 安心的繼續上不良網站
            std::cout << "老子" << name_ << "怕誰了?老闆不存在的!" << std::endl;
        }
    }

    std::string getName() {
        return name_;
    }

private:
    std::string name_;
};

#endif

被觀察者:“Subject.h”

#ifndef __SUBJECT_H__
#define __SUBJECT_H__

#include "Observer.h"

/*
 * 抽象主題類
 * 把所有的觀察者對象的引用保存在一個聚集裏,每個主題都可以有任意數量
 * 的觀察者,抽象主題提供一個接口,可以增加和刪除觀察者對象
 */
class CSubject
{
public:
    virtual ~CSubject() {}

    // 添加觀察者抽象函數
    virtual void RegisterObserver(CObserver *observer) = 0;

    // 移除觀察者抽象函數
    virtual void RemoveObserver(CObserver *observer) = 0;

    // 通知所有的觀察者
    virtual void NotifyObserver(const std::string& msg) = 0;
};

#endif

被觀察者的實體類: “ConcreteSubject.h”

溫馨提示 下面代碼中用到了一些C++11新特性,不大瞭解的讀者可到網上查閱

#ifndef __CONCRETESUBJECT_H__
#define __CONCRETESUBJECT_H__

#include "Subject.h"
#include "Observer.h"
#include <list>
#include <string>
#include <memory>

/*
 * 具體的主題實現類
 * 將有關狀態存入具體的觀察者對象
 * 在具體主題的內部狀態改變時給所有登記過的觀察者發出通知
 */
class ConcreteSubject : public CSubject 
{
public:
    ConcreteSubject() {
        pObservers_ = new std::list<std::shared_ptr<CObserver>>();
    }

    void RegisterObserver(CObserver *observer) {
        pObservers_->push_back(std::shared_ptr<CObserver>(observer));
    }

    void RemoveObserver(CObserver *observer) {
        if (pObservers_->size() > 0) {
            pObservers_->remove(std::shared_ptr<CObserver>(observer));
        }
    }

    void NotifyObserver(const std::string& msg) {
        std::cout << "羣消息: " << msg << std::endl;
        auto iter = pObservers_->begin();
        for (; iter != pObservers_->end(); ++iter) {
            (*iter)->Update(msg);
        }
    }

private:
    std::list<std::shared_ptr<CObserver>> *pObservers_;
};

#endif

在"main.cc"中運行觀察者模式

#include <iostream>
#include "../Include/ConcreteObserver.h"
#include "../Include/ConcreteSubject.h"

using namespace std;

int main(void)
{
    // 初始化觀察者對象
    ConcreteObserver *A = new ConcreteObserver("小張");
    ConcreteObserver *B = new ConcreteObserver("小亮");
    ConcreteObserver *C = new ConcreteObserver("小紅");

    // 初始化主題類
    ConcreteSubject *subject = new ConcreteSubject();

    // 向主題類中添加觀察者對象
    subject->RegisterObserver(A);
    subject->RegisterObserver(B);
    subject->RegisterObserver(C);

    // 主題類執行羣發操作(可以理解爲前臺小姐姐通知員工老闆來沒來)
    subject->NotifyObserver("老闆來了");
    subject->NotifyObserver("嚇嚇你們");

    system("Pause");

    return 0;
}

**溫馨提示:**在linux下博主使用的是cmake來編譯程序,如果對cmake不大瞭解的讀者可以看後面的cmake腳本,如果想要更深入的學習的話請自己去查閱相關資料吧。

CMake編寫

下面來看看我的文件目錄吧:

  • ObserverModel
    • build
    • Include
      • ConcreteObserver.h
      • ConcreteSubject.h
      • Observer.h
      • Subject.h
    • src
      • main.cc
      • CMakeLists.txt
    • CMakeLists.txt

我們先來看看ObserverModel 目錄下的CMakeLists.txt

PROJECT(TEST)
ADD_SUBDIRECTORY(src)

下面來編寫src目錄下的CMakeLists.txt腳本

SET(SOULIST_SRC main.cc)

ADD_EXECUTABLE(test ${SOULIST_SRC})

# 用以編譯shared_ptr
if(UNIX)  
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=gnu++0x")  
endif()  

這時我的代碼跟腳本就已經全部完成了,下面我們來運行代碼吧
將終端來到ObserverModel /build目錄下,編輯:

cmake ..
make

這時在build目錄下的src文件夾下就可以看到可執行文件test了,編輯:

./test

即可運行程序了

看完後是不是發現其實觀察者模式其實挺簡單的,主要就是將觀察者添加到被觀察者的list鏈表裏面,每當被觀察者(美麗大方的前臺小姐姐)產生變化後(在實際開發中可能是用戶的某一個操作然後產生了消息)被觀察者通過調用觀察者的Update方法將所產生的消息更新到觀察者裏面(在實際開發中觀察者可能是客戶端的界面,當接收到這個消息後,我們通過Update方法便可對界面作出響應的改變)

寫了博客後我發現其實把自己曾經所學的知識再整理出來是非常不錯的體驗,每每寫到類似的代碼總會作出一些代碼的優化,在提供別人學習的同時,又能進一步的鞏固知識,強化自己!

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