C++设计模式 - 观察者模式

2021-12-27 08:40:05 浏览数 (1)

观察者模式

观察者模式是一种行为设计模式,主要用于实现一种订阅机制,可在目标事件发生变化时告知所有观察此事件的对象,使观察者做出对应的动作。通常是通过调用各观察者所提供的方法来实现。

UML类图

观察者模式

CObserver类,为观察者抽象类,为具体观察者定义标准接口:

  • Update() 用于更新自身行为,由具体主题调用。
  • GetName用于获取定义的字符,用于标识各个对象。

CSubject类,为主题抽象类,主要为具体的主题类定义标准的接口。主要接口:

  • Register(): 注册观察者
  • Unregister(): 注销指定观察者
  • Notify(): 通知所有观察者

CRadio类,具体的主题类(CSubject子类)。例:无线电。

  • 实现注册/注销观察当前主题的接口Register/Unregister,供观察者调用。
  • 实现通知接口Notify,用于主题发布时,通知所有的观察者。

**CInterphone、CPhone **类,具体的观察者类。例:对讲机、手机

  • 实现消息传递接口Update,供具体主题对象调用,用于通知主题内容。

场景列举

如上场景,现某处无线电在特定频道发出广播,所有关注此频道设备都会接收到此信号。

具体主题为Radio,其内部需要在主题发布时,通知所有观察者

代码语言:javascript复制
class CRadio : public CSubject
{
public:
    CRadio()
    {
        mObserverList.clear();
    }

    ~CRadio()
    {

    }

    int Register(CObserver *pObsr)
    {
        if (nullptr != pObsr) {
            mObserverList.push_back(pObsr);
        } else {
            OBSERVER_LOGE("Register failed!n");
            return -1;
        }

        return 0;    
    }

    int Unregister(CObserver *pObsr)
    {
        if (nullptr != pObsr) {
            mObserverList.remove(pObsr);
        } else {
            OBSERVER_LOGE("Unregister failed!n");
            return -1;
        }

        return 0;
    }

    int Notify(void *pMsg)
    {
        list<CObserver *>::iterator it;
        SMsgTransfer *pGmsg = (SMsgTransfer *)pMsg;

        OBSERVER_LOG("Send radio: [0x%x] [%s]n", pGmsg->type, pGmsg->buf);
        // loop: 向监听此事件的所有观察者发布主题
        for (it = mObserverList.begin(); it != mObserverList.end(); it  ) {
            (*it)->Update(pMsg);
        }

        return 0;
    }

private:
    list <CObserver *> mObserverList;
};

具体观察者为可接收到此无线电的设备:手机、对讲机

代码语言:javascript复制
/* 监听无线电的设备: 对讲机 */
class CInterphone : public CObserver
{
public:
    explicit CInterphone(string value) 
    {
        mName = value;
    }

    ~CInterphone()
    {

    }

    string GetName()
    {
        return mName;
    }

    int Update(void *pMsg)
    {
        SMsgTransfer *msg = (SMsgTransfer *)pMsg;
        OBSERVER_LOG("%s receive: [0x%x] [%s]n", mName.c_str(), msg->type, msg->buf);
        return 0;
    }

private:
    string mName;
};

/* 监听无线电的设备: 手机 */
class CPhone : public CObserver
{
public:
    explicit CPhone(string value) 
    {
        mName = value;
    }

    ~CPhone()
    {

    }

    string GetName()
    {
        return mName;
    }

    int Update(void *pMsg)
    {
        if (pMsg != NULL)
        {
            SMsgTransfer *msg = (SMsgTransfer *)pMsg;
            OBSERVER_LOG("%s receive: [0x%x] [%s]n", mName.c_str(), msg->type, msg->buf);
        } else {
            OBSERVER_LOG("%s receive messages cannot be resolved!n", mName.c_str());
        }

        return 0;
    }

private:
    string mName;
};

客户端代码。使用Radio和设备形成应用场景

代码语言:javascript复制
CRadio theRadio;
CPhone thePhoneUser1("Phone user1");
CPhone thePhoneUser2("Phone user2");
CInterphone theInterUser1("Interphone user1");

static int init()
{
    // 注册监听无线电的设备,可放在观察者类初始化内部
    theRadio.Register(&theInterUser1);
    theRadio.Register(&thePhoneUser1);
    theRadio.Register(&thePhoneUser2);

    return 0;
}

int main(int argc, char *argv[])
{
    init();

    // 发出SOS无线电
    OBSERVER_LOG("------------------------------------------n");
    SMsgTransfer msg1 = {191519, "SOS"};
    theRadio.Notify(&msg1);

    OBSERVER_LOG("------------------------------------------n");
    SMsgTransfer msg2 = {131313, "HELLO"};
    theRadio.Notify(&msg2);

    return 0;
}

输出:

代码语言:javascript复制
$ ./a.out 
------------------------------------------
Send radio: [0x2ec1f] [SOS]
Interphone user1 receive: [0x2ec1f] [SOS]
Phone user1 receive: [0x2ec1f] [SOS]
Phone user2 receive: [0x2ec1f] [SOS]
------------------------------------------
Send radio: [0x200f1] [HELLO]
Interphone user1 receive: [0x200f1] [HELLO]
Phone user1 receive: [0x200f1] [HELLO]
Phone user2 receive: [0x200f1] [HELLO]

总结

  • 观察者将耦合转移到抽象类之间,实现耦合倒转,具体的实现类无需关注此耦合,只需要完成分内的工作即可。主题无需关注观察者的数量及行为,观察者也不需了解主题具体的逻辑。
  • 满足开闭原则,当需要新增新的观察者时,只需要增加具体的观察者类即可,无需修改原有代码。
  • 一般情况下,观察者会被应用于不同进程之间。例如,在电视机的系统中,电视需要待机。此时当发生待机动作时,所有需要在待机时做出相应动作的进程都需要关注待机事件(例:APP需要保存当前数据)。此场景下,待机就为主题,其他进程为观察者。且需要增加一个通知类来维护广播的机制,此类需要具备跨进程通信和观察者机制。
  • 当系统中许多实例或组件需要关注同一个事件时,可采用观察者模式。

0 人点赞