• [C++设计模式]observer 观察者模式


    有这么一种松耦合的需求:

    有一些类的对象对类A对象的状态变化非常感兴趣,不会改变类A的对象,也不会被类A的对象改变,想以一种较小的代价观察对类A对象状态变化。

    以下的几种方式也能实现上述目的

    (1)通过类的继承来共同管理和维护一些感兴趣的数据或者状态,可是耦合度大。不易扩展和维护。

    (2)通过调用被观察者的getter方法获取数据,这个还是直接的对象调用。

    上述两种方法将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。不管是观察者“观察”观察对象。

    还是被观察者将自己的改变“通知”观察者,都不应该直接调用。



    观察者模式(有时又被称为公布-订阅Subscribe>模式、模型-视图View>模式、源-收听者Listener>模式或从属者模式)中,一个目标物件管理全部相依于它的观察者物件,而且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

    实现观察者模式有非常多形式,比較直观的一种是使用一种“注冊——通知——撤销注冊”的形式。陈硕的《linux多线程服务端编程 使用muduo c+网络库》中有一个多人聊天的样例。在服务端程序中,要把一个client发来的消息分发给全部的client,也能够看待是观察者模式的典型案例,TCP的连接视为注冊,消息的分发视为通知,TCP连接的断开视为撤销注冊。



    观察者(Observer)将自己注冊到被观察对象(Subject)中。被观察对象将观察者存放在一个容器(Container)里。


    被观察

    被观察对象发生了某种变化(如图中的SomeChange),从容器中得到全部注冊过的观察者,将变化通知观察者。
    撤销观察

    观察者告诉被观察者要撤销观察。被观察者从容器中将观察者去除。


    观察者将自己注冊到被观察者的容器中时,被观察者不应该过问观察者的详细类型,而是应该使用观察者的接口。这种长处是:假定程序中还有别的观察者,那么仅仅要这个观察者也是同样的接口实现就可以。一个被观察者能够相应多个观察者。当被观察者发生变化的时候,他能够将消息一一通知给全部的观察者。

    基于接口。而不是详细的实现——这一点为程序提供了更大的灵活性。


    C++样例

    #include<iostream>
    #include<set>
    #include<string>
    usingnamespacestd;
    /////////////////////抽象模式定义
    class CObservable;
    //观察者,纯虚基类
    classCObserver
    {
    public:
    CObserver::CObserver(){};
    virtualCObserver::~CObserver(){};
    //当被观察的目标发生变化时,通知调用该方法
    //来自被观察者pObs,扩展參数为pArg
    virtualvoidUpdate(CObservable*pObs,void*pArg=NULL)=0;
    };
    //被观察者,即Subject
    classCObservable
    {
    public:
    CObservable():m_bChanged(false){};
    virtual~CObservable(){};
    //注冊观察者
    voidAttach(CObserver*pObs);
    //注销观察者
    voidDetach(CObserver*pObs);
    //注销全部观察者
    voidDetachAll();
    //若状态变化。则遍历观察者,逐个通知更新
    voidNotify(void*pArg=NULL);
    //測试目标状态是否变化
    boolHasChanged();
    //获取观察者数量
    intGetObserversCount();
    protected:
    //设置状态变化!!!必须继承CObservable才干设置目标状态
    voidSetChanged();
    //初始化目标为未变化状态
    voidClearChanged();
    private:
    boolm_bChanged;//状态
    set<CObserver*>m_setObs;//set保证目标唯一性
    };
    /////////////////////抽象模式实现
    voidCObservable::Attach(CObserver*pObs)
    {
    if(!pObs)return;
    m_setObs.insert(pObs);
    }
    voidCObservable::Detach(CObserver*pObs)
    {
    if(!pObs)return;
    m_setObs.erase(pObs);
    }
    voidCObservable::DetachAll()
    {
    m_setObs.clear();
    }
    voidCObservable::SetChanged()
    {
    m_bChanged=true;
    }
    voidCObservable::ClearChanged()
    {
    m_bChanged=false;
    }
    boolCObservable::HasChanged()
    {
    returnm_bChanged;
    }
    intCObservable::GetObserversCount()
    {
    returnm_setObs.size();
    }
    voidCObservable::Notify(void*pArg/*=NULL*/)
    {
    if(!HasChanged())return;
    cout<<"notifyobservers…"<<endl;
    ClearChanged();
    set<CObserver*>::iteratoritr=m_setObs.begin();
    for(;itr!=m_setObs.end();itr++)
    {
    (*itr)->Update(this,pArg);
    }
    }
    /////////////////////详细应用类定义和实现
    //bloger是公布者。即被观察者(subject)
    classCBloger:publicCObservable
    {
    public:
    voidPublish(conststring&strContent)
    {
    cout<<"blogerpublish,content:"<<strContent<<endl;
    SetChanged();
    Notify(const_cast<char*>(strContent.c_str()));
    }
    };
    //portal是公布者。即被观察者(subject)
    classCPortal:publicCObservable
    {
    public:
    voidPublish(conststring&strContent)
    {
    cout<<"portalpublish,content:"<<strContent<<endl;
    SetChanged();
    Notify(const_cast<char*>(strContent.c_str()));
    }
    };
    //RSS阅读器,观察者
    classCRSSReader:publicCObserver
    {
    public:
    CRSSReader(conststring&strName):m_strName(strName){}
    virtualvoidUpdate(CObservable*pObs,void*pArg=NULL)
    {
    char*pContent=static_cast<char*>(pArg);
    //观察多个目标
    if(dynamic_cast<CBloger*>(pObs))
    {
    cout<<m_strName<<"updatedfrombloger,content:"<<pContent<<endl;
    }
    elseif(dynamic_cast<CPortal*>(pObs))
    {
    cout<<m_strName<<"updatedfromportal,content:"<<pContent<<endl;
    }
    }
    private:
    stringm_strName;
    };
    //Mail阅读器,观察者
    classCMailReader:publicCObserver
    {
    public:
    CMailReader(conststring&strName):m_strName(strName){}
    virtualvoidUpdate(CObservable*pObs,void*pArg=NULL)
    {
    char*pContent=static_cast<char*>(pArg);
    if(dynamic_cast<CBloger*>(pObs))
    {
    cout<<m_strName<<"updatedfrombloger,content:"<<pContent<<endl;
    }
    if(dynamic_cast<CPortal*>(pObs))
    {
    cout<<m_strName<<"updatedfromportal,content:"<<pContent<<endl;
    }
    }
    private:
    stringm_strName;
    };
    /////////////////Main
    intmain()
    {
    //目标(被观察者)
    CBloger*pBloger=newCBloger();
    CPortal*pPortal=newCPortal();
    //观察者.一个观察者能够观察多个目标
    CRSSReader*pRssReader=newCRSSReader("rssreader");
    CMailReader*pMailReader=newCMailReader("mailreader");
    pBloger->Attach(pRssReader);//bloger注冊观察者
    pBloger->Attach(pMailReader);//bloger注冊观察者
    pPortal->Attach(pRssReader);//portal注冊观察者
    pPortal->Attach(pMailReader);//portal注冊观察者
    //博客公布信息
    pBloger->Publish("博客分享设计模式");
    cout<<endl;
    //门户公布信息
    pPortal->Publish("门户分享设计模式");
    cout<<"
    portaldetachedmailreader"<<endl;
    pPortal->Detach(pMailReader);
    cout<<"portalobserverscount:"<<pPortal->GetObserversCount()<<endl<<endl;
    pPortal->Publish("门户分享设计模式");
    system("pause");
    return0;
    }


  • 相关阅读:
    temp
    JAVA&nbsp;存储空间 寄存器 堆栈 堆…
    数据类型、变量、数组类
    ubuntu ARP 防御
    详解 JAVA 创建对象 NEW
    Eclipse常见问题集锦
    解决error:2014 Commands out of sync; you can't run this command now
    关于MySql5“data too long for column”问题的探解
    Notepad++编辑Pyhton文件的自动缩进的问题(图文)
    mysql 'latin1' codec can't encode characters的问题
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/6984300.html
Copyright © 2020-2023  润新知