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方法便可对界面作出响应的改变)

写了博客后我发现其实把自己曾经所学的知识再整理出来是非常不错的体验,每每写到类似的代码总会作出一些代码的优化,在提供别人学习的同时,又能进一步的巩固知识,强化自己!

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