C++设计模式——观察者模式(Observer Pattern)

微信公众号:幼儿园的学霸

目录

前言

观察者模式面向的需求是:A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者、小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会错过任何瞬间。程序里的观察者和这种真正的【观察】略有不同,观察者不需要时刻盯着被观察者(例如A不需要每隔1ms就检查一次B的状态),而是采用注册(Register)或者成为订阅(Subscribe)的方式告诉被观察者:我需要你的某某状态,你要在它变化时通知我。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。

定义

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
该模式属于行为型模式。

发布-订阅模式和观察者模式两者之间能否划上等号,网上的看法分为两极,众说纷纭。

看法一:发布订阅模式里的Publisher,就是观察者模式里的Subject,而Subscriber,就是Observer。Publisher变化时,就主动去通知Subscriber
看法二:在发布订阅模式里,发布者,并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互不相识,两者完全不耦合。他们的交流是通过第三者,也就是在消息队列里面的Broker

我的看法: 模式区别除了实现外,最重要的是模式的目的性。显然两者都实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,从目的来看,两者是一样的。

有两大类(主题和观察者)一共四个角色
从上面我们可以看到,这里面包含了:

  • 抽象主题/抽象被观察者(Subject)角色:将所有观察者对象保存在一个集合中,可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象
  • 具体主题/具体被观察者(ConcreteSubject)角色:该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知
  • 抽象观察者(Observer)角色:它定义了一个更新接口,使得在得到主题/被观察者更新时通知自己
  • 具体观察者(ConcrereObserver)角色:实现抽象观察者定义的更新接口,以便在得到主题/被观察者更新时通知自己更新自身状态
    其UML类图如下:
    观察者模式UML类图

核心: 交互对象之间进行松耦合设计

代码示例

代码关键:
被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
观察者对象可以注册到被观察者的中,完成注册后可以检测被观察者的变化,接收被观察者的通知。当然观察者也可以被注销掉,停止对被观察者的监控。

#include <bits/stdc++.h>

//
//观察者模式
//

class Observer;
//抽象被观察者
class Subject {
public:
    Subject() : m_nState(0) {}

    virtual ~Subject() = default;

    virtual void Attach(const std::shared_ptr<Observer> pObserver) = 0;

    virtual void Detach(const std::shared_ptr<Observer> pObserver) = 0;

    virtual void Notify() = 0;

    virtual int GetState() { return m_nState; }

    void SetState(int state) {
        std::cout << "Subject updated !" << std::endl;
        m_nState = state;
    }

protected:
    std::list<std::shared_ptr<Observer>> m_pObserver_list;
    int m_nState;
};

//抽象观察者
class Observer {
public:
    virtual ~Observer() = default;

    Observer(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : m_pSubject(pSubject), m_strName(name) {}

    virtual void Update() = 0;

    virtual const std::string &name() { return m_strName; }

protected:
    std::shared_ptr<Subject> m_pSubject;
    std::string m_strName;
};

//具体被观察者
class ConcreteSubject : public Subject {
public:
    void Attach(const std::shared_ptr<Observer> pObserver) override {
        auto iter = std::find(m_pObserver_list.begin(), m_pObserver_list.end(), pObserver);
        if (iter == m_pObserver_list.end()) {
            std::cout << "Attach observer" << pObserver->name() << std::endl;
            m_pObserver_list.emplace_back(pObserver);
        }

    }

    void Detach(const std::shared_ptr<Observer> pObserver) override {
        std::cout << "Detach observer" << pObserver->name() << std::endl;
        m_pObserver_list.remove(pObserver);
    }

    //循环通知所有观察者
    void Notify() override {
        auto it = m_pObserver_list.begin();
        while (it != m_pObserver_list.end()) {
            (*it++)->Update();
        }
    }
};


//具体观察者1
class Observer1 : public Observer {
public:
    Observer1(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : Observer(pSubject, name) {}

    void Update() override {
        std::cout << "Observer1_" << m_strName << " get the update.New state is: "
                  << m_pSubject->GetState() << std::endl;
    }
};

//具体观察者2
class Observer2 : public Observer {
public:
    Observer2(const std::shared_ptr<Subject> pSubject, const std::string &name = "unknown")
            : Observer(pSubject, name) {}

    void Update() override {
        std::cout << "Observer2_" << m_strName << " get the update.New state is: "
                  << m_pSubject->GetState() << std::endl;
    }
};


int main() {

    std::shared_ptr<Subject> pSubject = std::make_shared<ConcreteSubject>();// 创建被观察者

    // 创建观察者
    std::shared_ptr<Observer> pObserver1_1 = std::make_shared<Observer1>(pSubject, "1");
    std::shared_ptr<Observer> pObserver1_2 = std::make_shared<Observer1>(pSubject, "2");
    std::shared_ptr<Observer> pObserver1_3 = std::make_shared<Observer1>(pSubject, "3");

    std::shared_ptr<Observer> pObserver2_4 = std::make_shared<Observer2>(pSubject, "4");
    std::shared_ptr<Observer> pObserver2_5 = std::make_shared<Observer2>(pSubject, "5");
    std::shared_ptr<Observer> pObserver2_6 = std::make_shared<Observer2>(pSubject, "6");

    // 注册观察者
    pSubject->Attach(pObserver1_1);
    pSubject->Attach(pObserver1_2);
    pSubject->Attach(pObserver1_3);
    pSubject->Attach(pObserver2_4);
    pSubject->Attach(pObserver2_5);
    pSubject->Attach(pObserver2_6);

    pSubject->SetState(2);// 改变状态
    pSubject->Notify();

    std::cout << std::string(50, '-') << std::endl;

    // 注销观察者
    pSubject->Detach(pObserver1_1);
    pSubject->Detach(pObserver1_2);

    pSubject->SetState(3);
    pSubject->Notify();

    return 0;
    //运行结果如下:
    //Attach observer1
    //Attach observer2
    //Attach observer3
    //Attach observer4
    //Attach observer5
    //Attach observer6
    //Subject updated !
    //Observer1_1 get the update.New state is: 2
    //Observer1_2 get the update.New state is: 2
    //Observer1_3 get the update.New state is: 2
    //Observer2_4 get the update.New state is: 2
    //Observer2_5 get the update.New state is: 2
    //Observer2_6 get the update.New state is: 2
    //--------------------------------------------------
    //Detach observer1
    //Detach observer2
    //Subject updated !
    //Observer1_3 get the update.New state is: 3
    //Observer2_4 get the update.New state is: 3
    //Observer2_5 get the update.New state is: 3
    //Observer2_6 get the update.New state is: 3

}

总结

观察者模式和中介模式

两个模式的意图还是非常相像的:
1.都属于行为型模式
2.都为了处理一对多的关系
3.UML实现基本相同,都有集合管理业务对象的集合,都有循环通知的方法,符合单一职责原则。

but,两者之间区别还是蛮大的:
中介模式中存在Mediator和Colleague两个角色,Mediator一般不会主动发起事件去通知Colleague,而Colleague具有发送和接收消息的两种能力。Mediator一般不会是消息的源头,也不会是消息传输的终点,它充当中转站的角色。而Colleague既可以是消息的发起者,也可以是消息传输的接收者,这意味着消息是双向的。Colleague可以存在多个,中介者(mediator)强调的是同事(colleague)类之间的交互。
反观观察者模式,消息的发起者只有一个,即Subject,所有Observer都关注Subject的消息,Subject只能发消息,Observer只能收消息,即消息的通知是单向的。观察者模式强调的是目标改变后对观察者进行统一的通讯,即被观察者与观察者之间的交互,所有的观察者都是一样的

优缺点

  • 优点
    1.观察者和被观察者是抽象耦合的
    2.建立了一套触发机制
  • 缺点
    1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
    2.如果观察者和观察目标间有循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
    3.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

适用场景及应用示例

  • 适用场景
    1.一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
    2.一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度
    3.一个对象必须通知其他对象,而并不知道这些对象是谁
    4.需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制
    5.跨系统的消息变换场景,如消息队列的处理机制

  • 应用实例
    1.手机丢了,委托别人给其他人发消息通知
    2.通知老师/老板来了
    3.拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
    4.猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者
    5.西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作

观察者模式解除了主体和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

参考资料

1.简说设计模式——观察者模式
2.观察者模式
3.设计模式之观察者模式
4.设计模式专辑——中介模式、观察者模式的比较



下面的是我的公众号二维码图片,按需关注。
图注:幼儿园的学霸

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐