前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++11】 改进我们的设计模式---观察者模式

【C++11】 改进我们的设计模式---观察者模式

作者头像
CPP开发前沿
发布2021-11-16 14:31:07
9570
发布2021-11-16 14:31:07
举报
文章被收录于专栏:CPP开发前沿CPP开发前沿

观察者模式(Observer Pattern)主要解决的是当对象间存在一对多关系时当一个对象被修改,会自动通知依赖它的其它对象。在设计模式中观察者模式属于行为型模式。

1 经典观察者模式

设计模式相关的书籍中,介绍观察者模式时大都用下面的图:

如上,上面的类图就是一个简单的观察者模式,上面的类说明如下:

  • Subject:主题,通俗的理解为信息的发布者,提供了三个接口,分别是添加观察者,删除观察者,消息通知;
  • ConcreteSubject:具体主题,Subject的派生类,包含两个接口,设置和获取状态;
  • Obersver:观察者,提供了一个通知接口。
  • ConcreteObersver:Onersver的派生类,实现了基类的接口,维护一个指向ConcreteSubject对象的引用。

实现上面这个观察者模式的代码如下:

代码语言:javascript
复制
class Observer{
public:
    virtual void Update(int) = 0;
};
 
class Subject{
public:
    virtual void Attach(Observer *) = 0;
    virtual void Detach(Observer *) = 0;
    virtual void Notify() = 0;
};
 
class ConcreteObserver : public Observer{
public:
    ConcreteObserver(Subject *pSubject) : m_pSubject(pSubject){};
    void Update(int value){
        cout << "接收到信息通知:" << value << endl;
    }
private:
    Subject *m_pSubject;
};
 
class ConcreteSubject : public Subject{
public:
    void Attach(Observer *pObserver);
    void Detach(Observer *pObserver);
    void Notify();
    void SetState(int state){
        m_iState = state;
    }
    
    void GetState(){
        return m_iState;
    }
private:
    std::list<Observer *> m_ObserverList;
    int m_iState;
};
 
void ConcreteSubject::Attach(Observer *pObserver){
    m_ObserverList.push_back(pObserver);
}
 
void ConcreteSubject::Detach(Observer *pObserver){
    m_ObserverList.remove(pObserver);
}
 
void ConcreteSubject::Notify(){
    std::list<Observer *>::iterator it = m_ObserverList.begin();
    while (it != m_ObserverList.end()){
        (*it)->Update(m_iState);
        ++it;
    }
}

如上面代码所示,实现了一个经典的观察者模式,但是在实际的使用时,这种实现方式并不灵活,有很多的限定,最明显的两个限制是:

  • 需要使用继承关系,继承的缺点有很多,如:继承关系会造成派生类中产生冗余代码,降低了代码的灵活性,且需要实现父类中定义的方法;和基类耦合性太强,一旦使用继承,派生类就拥有了父类中所有方法。
  • 主题和观察者之间的接口定义方式是固定的,不能适应后续随着业务发展带来的新的交互方式的变化。

2 改进后的观察者模式

为了解决经典观察者模式中的问题,可以使用C++11中提供的新的语言特性,如将消息通知接口进行参数化和使用std::function函数绑定解决类的继承问题,通过完美转发和可变参数模板消除接口接口变化的影响。经过改进后的观察模式有新增观察者时不需要继承基类。只要新增一个事件类型即可。

在本次代码实现中,我们不希望新增的事件类型能够进行复制,因此,在实现时将会用=default和=delete标识符对类的特殊函数进行限制,改进后的观察者模式代码如下:

代码语言:javascript
复制
class NonCopyable{
protected:
    NonCopyable()=default;
    ~NonCopyable()=default;
    NonCopyable(const NonCopyable &)=delete;
    NonCopyable & operator =(const NonCopyable &) = delete;
};

template <typename Func>
class Event:NonCopyable{
public:
    Event()=default;
    ~Event()=delete;
    //注册观察者,支持右值引用
    int Attach(Func && f){
        return Assign(f);
    }
     //注册观察者,支持左值引用
    int Attach(Func & f){
        return Assign(f);
    }
    //移除观察者
    void Deatach(int iKey){
        m_mapOberserve.erase(iKey);
    }
    //通知接口
    template <typename... Args>
    void Notify(Args&&... args){
        for(auto& it:m_mapOberserve)
        {
            it.second(std::forward<Args>(args)...);
        }
    }
private:
    template <typename F>
    int Assign(F && f){
        int k = m_iObersevre++;
        m_mapOberserve.emplace(k,std::forward<F>(f));
        return k;
    }
    
    int m_iObersevre = 0;
    std::map<int,Func> m_mapOberserve;
};

如上面的代码所示,使用C++11后的观察者模式通过维护一个泛型函数列表消除了类继承导致的代码耦合,使得只需要传入观察者调用函数既可。通知接口通过可变参数模版也实现了任意参数的输入消除了通知接口不适应业务发展变化的影响。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP开发前沿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档