[8 使用C++11改进我们的模式] 8.2 改进观察者模式

本文介绍了C++11中对观察者模式的改进,通过使用std::function和可变参数模板,解决了传统观察者模式中的继承耦合问题,实现了更灵活的观察者注册和通知机制。此外,还引入了NonCopyable类以防止不必要的复制。示例代码展示了如何使用C++11特性实现一个通用的事件发布系统,并提供了测试用例来说明其工作原理。
摘要由CSDN通过智能技术生成

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式,它是对象行为型模式。


观察者模式是一种对象行为型模式,其主要优点如下。

1 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2 目标与观察者之间建立了一套触发机制。


它的主要缺点如下。

1 目标与观察者之间的依赖关系并没有完全解除。
2 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

类图如下:

一个简单的观察者模式如下:

class Subject;

// 观察者接口类
class Observer
{
public:
    virtual ~Observer();
    // 即类图中Response函数
    virtual void Update(Subject* theChangedSubject) = 0;
protected:
    // 只有子类内部的其他函数可以调用protected函数
    Observer();
};

// 目标接口类
class Subject {
public:
    virtual ~Subject();
    virtual void Attach(Observer*);
    virtual void Detach(Observer*);
    virtual void Notify();
protected:
    Subject();
private:
    // 观察者列表
    List<Observer*> _observers;
};

// 增加观察者
void Subject::Attach(Observer* o) {
    _observers->append(o);
}
// 删除观察者
void Subject::Detach(Observer* o) {
    _observers->remove(o);
}
// 目标类发出通知,通知所有观察者
void Subject::Notify() {
    ListIterator<Observer*> i(_observers);

    for(i.first();!i.isDone();i.next()) {
        i.currentItem()->Update(this);
    }
}

这种实现方式主要两个限定:

(1)需要继承,继承是强对象关系,不够灵活

(2)观察者被通知的接口参数不支持变化(Observer的Update接口)

C++11改进有两点:

(1)通过std::fucntion来代替继承

(2)通过可变参数模板使得被通知接口参数化

C++11提供了default和delete,使我们可以方便地实现一个NonCopyable类。如果希望类不被复制,则直接从这个NonCopyable类派生。

NonCopyable类的实现:

class NonCopyable
{
protected:
    // 将函数声明为显示构造函数
    NonCopyable() = default;
    ~NonCopyable() = default;
    // 禁止复制构造
    NonCopyable(const NonCopyable&) = delete;
    // 禁止赋值
    NonCopyable& operator = (const NonCopyable&) = delete;
};

C++11改进后的观察者模式:

#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;

template<typename Func>
class Events : NonCopyable
{
public:
    Events();
    ~Events();
    // 注册观察者,支持右值引用
    int Connect(Func&& f)
    {
        return Assign(f);
    }
    // 注册观察者
    int Connect(const Func& f)
    {    
        return Assign(f);
    }
    // 移除观察者
    int DisConnect(int key)
    {
        m_connections.erase(key);
    }
    // 通知所有观察者
    template<typename... Args>
    void Notify(Args&&... args)
    {
        for (auto& it:m_connections)
        {
            it.second(std::forward<Args>(args)...);
        }
    }

private:
    template<typename F>
    int Assign(F&& f)
    {
        int k = m_observerId++;
        m_connections.emplace(k, std::forward(F)(f));
        return k;
    }    

    // 观察者编号
    int m_observerId = 0;
    // 观察者列表
    std::map<int, Func> m_connections;
};

测试代码如下:

struct stA
{
    int a, b;
    void print(int a,int b)
    {
        cout << "a," << a << " b," << b << endl;    
    }
};

void print(int a, int b)
{
    cout << "a:" << a << " b:" << b << endl;
}

int main(){
    Events<std::function<void(int,int)>> myevent;
    
    // 以函数方式注册观察者
    auto key = myevent.Connect(print);
    // lambda注册观察者
    stA t;
    auto lambdakey = myevent.Connect([&t](int a, int b){t.a = a; t.b = b;});
    // std::function注册观察者
    std::function<void(int,int)> f = std::bind(&stA::print, &t,         
        std::placeholders::_1, std::placeholders::_2);
    auto funckey = myevent.Connect(f);

    int a=1,b=2;
    // 广播所有观察者
    myevent.Notify(a,b);
    
    // 移除观察者
    myevent.Disconnect(key);
    myevent.Disconnect(lambdakey);
    myevent.Disconnect(funckey);

    return 0;
}

C++11改进后的观察者模式,内部维护了一个泛型函数map(m_connections),观察者只需将观察者函数注册进来,消除了继承导致的强耦合;也不要求观察者必须从某个类派生,当需要不同的观察者时,只需定义一个新的event即可。同时,通知接口使用了可变参数列表(Notify),支持任意参数。

参考网址:

http://c.biancheng.net/view/1390.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值