设计模式--观察者(Observer)模式


概念

观察者模式(Observer Pattern)是C++中常用的一种行为型设计模式,它定义了对象间的一种一对多的依赖关系。在这种模式中,当一个对象(被观察者,Subject)的状态发生改变时,所有依赖于它的对象(观察者,Observer)都会收到通知并自动更新

这种模式的结构通常包括四个部分:抽象主题(Subject)、抽象观察者(Observer)、具体主题(ConcreteSubject)和具体观察者(ConcreteObserver)。

  • 抽象主题(Subject):负责维护一个观察者列表,并提供添加、删除和通知观察者的接口。它并不知道具体的观察者是谁,只知道它们都实现了Observer接口。
  • 抽象观察者(Observer):为所有的具体观察者定义一个接口,当得到主题的通知时,进行自我更新。
  • 具体主题(ConcreteSubject):将有关状态存入各具体观察者对象。当具体主题的状态发生任何更改时,通知所有观察者。
  • 具体观察者(ConcreteObserver):实现Observer所要求的更新接口,以便使本身的状态与主题的状态相协调。

观察者模式的优点包括:

  1. 满足“开-闭原则”:新添加的观察者类不需要修改主题类的代码,满足软件设计的开闭原则。
  2. 高内聚、低耦合:被观察者和观察者之间建立一个抽象的耦合,两者可以属于不同的抽象化层次,被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
  3. 支持广播通讯:被观察者对象可以同时向多个观察者对象传送信息,消息是一对多关系。

然而,观察者模式的一个缺点是没有相应的机制使观察者知道所观察的对象是怎么发生变化的,而只是知道对象发生了变化。

在C++中,观察者模式特别适用于GUI组件编程,允许不同的UI在不与其他对象类耦合的前提下,对逻辑层事件做出不同反应。

请注意,设计模式的选择和使用应根据具体的问题和场景来决定,每种设计模式都有其适用的场景和限制。


示例

通过前向声明解决C++中两个头文件互相引用的问题

Observer.h

#include <iostream>
#include <string.h>

class Subject;

class Observer {
public:
    Observer(std::string name, Subject* sub) {
        this->name = name;
        this->sub = sub;
    }
    virtual void Update(){}

public:
    std::string name;
    Subject* sub;
};

class StockObserver : public Observer {
public:
    StockObserver(std::string name, Subject* sub) : Observer(name, sub) {}
    void Update();
};

class NBAObserver : public Observer {
public:
    NBAObserver(std::string name, Subject* sub) : Observer(name, sub) {}
    void Update();
};

Observer.cpp

#include "Observer.h"
#include "Subject.h"

void NBAObserver::Update() {
    std::cout << "看NBA的同事收到消息:" << sub->SubjectState << std::endl;
}

void StockObserver::Update() {
    std::cout << "看股票的同事收到消息:"<< sub->SubjectState << std::endl;
}

Subject.h

#include <iostream>
#include <cstring>
#include <list>

class Observer;

class Subject
{
public:
    virtual void Attach(Observer* observer){}   // 增加订阅
    virtual void Detach(Observer* observer){}   // 减少订阅
    virtual void Notify(){}     // 通知订阅者

    std::string SubjectState;
};

class Secretary : public Subject {
public:
    // 同事列表
    std::list<Observer*> observers;

public:
    void Attach(Observer *observer);
    void Detach(Observer *observer);
    void Notify();
};

Subject.cpp

#include "Subject.h"
#include "Observer.h"

// 增加
void Secretary::Attach(Observer* observer) {
    observers.push_back(observer);
}

// 减少
void Secretary::Detach(Observer* observer) {
    observers.remove(observer);
}

// 通知
void Secretary::Notify() {
    for (auto o : observers) {
        o->Update();
    }
}

main.cpp

// #include "test.h"
#include "Subject.h"
#include "Observer.h"

int main() {
    // 秘书
    Subject* secretary = new Secretary();

    // 同事
    Observer* tongshi1 = new StockObserver("张三", secretary);
    Observer* tongshi2 = new NBAObserver("李四", secretary);

    // 同事订阅的Subject
    secretary->Attach(tongshi1);
    secretary->Attach(tongshi2);

    // 老板回来了
    secretary->SubjectState = "warnning !!! : 老板回来了";
    // 向订阅者发出通知
    secretary->Notify();

    // NBA同事接触订阅
    std::cout << "NBA的同事取消了订阅" << std::endl;
    secretary->Detach(tongshi2);

    // 老板回来了
    secretary->SubjectState = "warnning !!! : 老板再次回来了";
    // 向订阅者发出通知
    secretary->Notify();

    return 0;
}

/*
看股票的同事收到消息:warnning !!! : 老板回来了
看NBA的同事收到消息:warnning !!! : 老板回来了
NBA的同事取消了订阅
看股票的同事收到消息:warnning !!! : 老板再次回来了
*/
posted @   guanyubo  阅读(163)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示