• 观察者模式Observer Pattern


    UML类图

    Observer(观察者基类)

    • 为那些在目标发生改变时需获得通知的对象定义一个更新接口

    Subject(观察目标基类,可以理解为通知者)

    • 目标知道它的观察者(它被那些观察者观察)。可以有任意多个观察者观察同一个目标;

    • 提供注册和注销观察者对象的接口。

    ConcreteObserver(具体的观察者)

    • 维护一个指向ConcreteSubject对象的引用;

    • 存储有关状态,这些状态应与目标的状态保持一致;

    • 实现Observer的更新接口以使自身状态与目标的状态保持一致。

    ConcreteSubject(具体的被观察者即观察目标)

    • 将有关状态存入各ConcreteObserver对象;

    • 当它的状态发生改变时,向它的各个观察者发出通知

    一、定义:观察者模式又被称为:发布订阅模式

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。可以理解为一个对象行为的改变,其相关联的对象都会得到通知,并自动产生对应的行为。

    二、适用场景:在以下任一情况下都可以使用观察者模式:

    • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立的改变和复用;

    • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变;

    • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁;也就是说,你不希望这些对象是紧密耦合的。

    • 凡是涉及到消息更新、广播机制、消息传递、链式触发都可以考虑使用观察者模式

    三、“观察”不是“直接调用”

    实现观察者模式的时候要注意,观察者和被观察对象之间的互动关系不能体现成类之间的直接调用,否则就将使观察者和被观察对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。不管是观察者“观察”观察对象,还是被观察者将自己的改变“通知”观察者,都不应该直接调用。

    四、实现形式:

    实现观察者模式有非常多形式,比较直观的一种是使用一种“注册——通知——撤销注册”的形式。

    观察者模式按照以下方式进行协作:

    • 当ConcreteSubject发生任何可能导致其观察者与其本身状态不一致的改变时,它将通知它的各个观察者;

    • 在得到一个具体目标的改变通知后,ConcreteObserver对象可向目标对象查询信息。ConcreteObserver使用这些信息以使它的状态与目标对象的状态一致

    五、应用实例:

    • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。

    • 西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

    • 对同一组数据进行统计分析时候, 我们希望能够提供多种形式的表示 (例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。这些表示都依赖于同一组数据, 我们当然需要当数据改变的时候, 所有的统计的显示都能够同时改变。

    六、实例代码

    点击查看代码
    /*
    *一般情况下,观察目标只有一个,即一对多的关系
    */
    // observer.h
    #include <list>
    
    //通知的消息类型
    enum class NotifyType
    {
    	Eat = 0,
    	Drink,
    	Play,
    	Work,
    };
    
    class Observer;
    
    //通知者(观察目标)
    class Subject
    {
    protected:
    	std::list<Observer*> m_observers;
    public:
    	void attach(Observer*);   //注册(添加一个观察者)
    	void detach(Observer*);   //注销(删除一个观察者)
    	virtual void notify() = 0; //通知
    };
    
    //观察者
    class Observer
    {
    public:
    	virtual void update(NotifyType type) = 0;  //更新
    	virtual ~Observer() {};
    };
    
    //具体的观察者:员工A
    class WorkerA :public Observer
    {
    	void update(NotifyType type) override;
    };
    
    //具体的观察者:员工B
    class WorkerB :public Observer
    {
    	void update(NotifyType type) override;
    };
    
    //具体的观察者:员工C
    class WorkerC :public Observer
    {
    	void update(NotifyType type) override;
    };
    
    //具体的通知者:组长
    class Leader :public Subject
    {
    public:
    	void notify() override;
    };
    
    //具体的通知者:经理
    class Manager :public Subject
    {
    public:
    	void notify() override;
    };
    
    //具体的通知者:老板
    class Boss :public Subject
    {
    public:
    	void notify() override;
    };
    
    // observer.cpp
    #include "observer.h"
    #include <iostream>
    
    void Subject::attach(Observer* obs)
    {
    	m_observers.emplace_back(obs);
    }
    
    void Subject::detach(Observer* obs)
    {
    	auto it = m_observers.begin();
    	while (it != m_observers.end())
    	{
    		if (*it == obs)
    			m_observers.erase(it);
    		it++;
    	}
    }
    
    void Leader::notify()
    {
    	//组长通知一起去洗脚
    	std::cout << "	组长发通知了
    ";
    	for (const auto& elem : m_observers)
    	{
    		elem->update(NotifyType::Play);
    	} 
    }
    
    void Manager::notify()
    {
    	//经理通知今晚加班
    	std::cout << "	经理发通知了
    ";
    	for (const auto& elem : m_observers)
    	{
    		elem->update(NotifyType::Work);
    	}
    }
    
    void Boss::notify()
    {
    	//老板通知今晚一起吃饭喝酒
    	std::cout << "	老板发通知了
    ";
    	for (const auto& elem : m_observers)
    	{
    		elem->update(NotifyType::Eat);
    		elem->update(NotifyType::Drink);
    	}
    }
    
    void WorkerA::update(NotifyType type)
    {
    	switch (type)
    	{
    	case NotifyType::Eat:
    		std::cout << "WorkerA:	去吃饭咯。
    ";
    		break;
    	case NotifyType::Drink:
    		std::cout << "WorkerA:	去喝酒咯。
    ";
    		break;
    	case NotifyType::Play:
    		std::cout << "WorkerA:	去洗脚咯。
    ";
    		break;
    	case NotifyType::Work:
    		std::cout << "WorkerA:	装作认真工作的样子。
    ";
    		break;
    	default:
    		break;
    	}
    }
    
    void WorkerB::update(NotifyType type)
    {
    	switch (type)
    	{
    	case NotifyType::Eat:
    		std::cout << "WorkerB:	我不去吃饭。
    ";
    		break;
    	case NotifyType::Drink:
    		std::cout << "WorkerB:	我不去喝酒。
    ";
    		break;
    	case NotifyType::Play:
    		std::cout << "WorkerB:	一起去洗脚咯。
    ";
    		break;
    	case NotifyType::Work:
    		std::cout << "WorkerB:	加你麻痹班!
    ";
    		break;
    	default:
    		break;
    	}
    }
    
    void WorkerC::update(NotifyType type)
    {
    	switch (type)
    	{
    	case NotifyType::Eat:
    		std::cout << "WorkerC:	我先回家了。
    ";
    		break;
    	case NotifyType::Drink:
    		std::cout << "WorkerC:	我不会喝酒。
    ";
    		break;
    	case NotifyType::Play:
    		std::cout << "WorkerC:	这次去哪洗脚?
    ";
    		break;
    	case NotifyType::Work:
    		std::cout << "WorkerC:	我要带孩子先溜了,你们好好加班。
    ";
    		break;
    	default:
    		break;
    	}
    }
    // main.cpp
    #include "observer.h"
    
    int main()
    {   
        //观察目标对象
        Subject* leader = new Leader();
        Subject* manager = new Manager();
        Subject* boss = new Boss();
    
        //观察者对象
        Observer* workerA = new WorkerA();
        Observer* workerB = new WorkerB();
        Observer* workerC = new WorkerC();
    
        //给观察目标添加观察者
        leader->attach(workerA);
        leader->attach(workerB);
        leader->attach(workerC);
    
        manager->attach(workerA);
        manager->attach(workerB);
        manager->attach(workerC);
    
        boss->attach(workerA);
        boss->attach(workerB);
        boss->attach(workerC);
    
        //观察目标状态改变发出通知
        leader->notify();
        manager->notify();
        boss->notify();
    
        return 0;
    }
    

    参考:
    设计模式之观察者模式(c++)

    观察者模式的程序实例C++

  • 相关阅读:
    C# 日期格式化的中的 正斜杠的问题
    JQuery中如何click中传递参数
    《启示录:打造用户喜爱的产品》—— 读书笔记
    json串的使用
    谷歌浏览器怎么调试js
    web页面布局思想
    js或者cs代码拼接html
    筛选DataTable数据的方法
    Ajax的简单小例子
    简单的javascript例子
  • 原文地址:https://www.cnblogs.com/mmmmmmmmm/p/14838575.html
Copyright © 2020-2023  润新知