侧边栏壁纸
博主头像
LittleAO的学习小站 博主等级

在知识的沙漠寻找绿洲

  • 累计撰写 125 篇文章
  • 累计创建 27 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

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

LittleAO
2024-09-03 / 0 评论 / 0 点赞 / 17 阅读 / 0 字
温馨提示:
本文最后更新于2024-09-03,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

资料来源:

https://www.bilibili.com/video/BV14z421k7hv/?spm_id_from=333.337.search-card.all.click&vd_source=439e60cb2efd384d15ece83188c1ad14

示例

某一系统能够根据股票价格实时更新电子广告牌和显示器的信息。描述很简单,我们可以简易的写一个C++程序模拟这种情况:

#include <iostream>

// 模拟显示器输出
struct Monitor 
{
    void print(int v)
    {
        std::cout<< "Monitor: " << v << std::endl;
    }
};

// 模拟广告牌输出
struct Billboard 
{
    void display(int v)
    {
        std::cout << "Billboard: " << v << std::endl;
    }
};

// 模拟一支股票
struct Stock
{
    int price = 20;
    Monitor* monitor;
    Billboard* board;
    Stock (Monitor* monitor, Billboard* board)
    : monitor(monitor), board(board) {}
    void setPrice(int v)
    {
        price = v;
        board->display(v);  // 广告牌更新
        monitor->print(v);  // 显示器更新
    }
};

int main()
{
    Monitor monitor;
    Billboard board;
    Stock stock { &monitor, &board };
    stock.setPrice(10);
    stock.setPrice(20);
}

当我们想要更新股票价格时,显示器和广告牌上的数据也会同步更新。上面的代码有着很显而易见的问题:可扩展性差。当我们想要加入新的显示方法时,就需要大改Stock类。紧耦合,Stock类中和其他类耦合度太高。耦合的类发生轻微改动,可能需要大范围的改动。

观察者模式

为了避免上述问题,我们可以使用观察者模式。下面是代码实现:

#include <corecrt_math.h>
#include <iostream>
#include <list>

struct Stock;   // 前置声明

struct Observer 
{
    Stock* stock;
    Observer(Stock* stock);
    virtual ~Observer();
    virtual void update(int) = 0;   //纯虚函数实现接口
};

// 模拟显示器输出
struct Monitor : Observer
{
    Monitor(Stock* stock) : Observer(stock) {}
    void print(int v)
    {
        std::cout<< "Monitor: " << v << std::endl;
    }
    void update(int v) override 
    {
        print(v);
    }
};

// 模拟广告牌输出
struct Billboard : Observer
{
    Billboard(Stock* stock) : Observer(stock) {}
    void display(int v)
    {
        std::cout << "Billboard: " << v << std::endl;
    }
    void update(int v) override
    {
        display(v);
    }
};

// 模拟一支股票
struct Stock
{
    int price = 20;
    std::list<Observer*> observerList;
    void attach(Observer* observer)
    {
        observerList.push_back(observer);
    }
    void detach(Observer* observer)
    {
        observerList.remove(observer);
    }
    void notify(int v)
    {
        for (auto observer : observerList)
        {
            observer->update(v);
        }
    }
    void setPrice(int v)
    {
        price = v;
        notify(v);
    }
};

Observer::Observer(Stock* stock) : stock(stock)
{
    stock->attach(this);
}

Observer::~Observer()
{
    stock->detach(this);
}

int main()
{
    Stock stock;
    Monitor monitor{ &stock };
    Billboard board{ &stock };
    stock.setPrice(10);
    stock.setPrice(20);
}

观察者模式有两个概念:观察者和被观察者。

  • 观察者是发现变化时,需要执行操作的对象;

  • 被观察者是变化的那个对象。

对于观察者,我们需要定义一个抽象类Observer。构造函数需要传入被观察对象的指针,其中有一个Update接口,用于派生类实现。

上面的例子中,显示器和广告牌就是两个观察者。他们继承Observer类后,需要实现接口,实现的接口内容就是更新自己的数据。

最重要的是被观察对象,被观察对象一定要实现三个接口:

  • attach:传入观察者对象指针,将其加入维护的链表中。

  • detach:在链表中移除对应的观察者对象指针。

  • notify:遍历链表,调用每个观察者的update接口。

这样就实现了观察者模式。当我们想要扩展功能,比如根据股票价格的高低做出一些行为时,只需要再创建一个类并且继承Observer即可,当股票价格发生变化,会正常通知每个类。减少了类之间的耦合。

如果想要实现观察多个目标,也可以为被观察者创建抽象类,下面是示例代码:

```cpp

#include <corecrt_math.h>
#include <iostream>
#include <list>

struct Stock;   // 前置声明

struct Observer 
{
    Stock* stock;
    Observer(Stock* stock);
    virtual ~Observer();
    virtual void update(int) = 0;   //纯虚函数实现接口
};

// 模拟显示器输出
struct Monitor : Observer
{
    Monitor(Stock* stock) : Observer(stock) {}
    void print(int v)
    {
        std::cout<< "Monitor: " << v << std::endl;
    }
    void update(int v) override 
    {
        print(v);
    }
};

// 模拟广告牌输出
struct Billboard : Observer
{
    Billboard(Stock* stock) : Observer(stock) {}
    void display(int v)
    {
        std::cout << "Billboard: " << v << std::endl;
    }
    void update(int v) override
    {
        display(v);
    }
};

struct Subject
{
    std::list<Observer*> observerList;
    virtual void attach(Observer*) = 0;
    virtual void detach(Observer*) = 0;
    virtual void notify(int) = 0;
};

// 模拟一支股票
struct Stock : Subject
{
    int price = 20;
    void attach(Observer* observer) override
    {
        observerList.push_back(observer);
    }
    void detach(Observer* observer) override
    {
        observerList.remove(observer);
    }
    void notify(int v)  override
    {
        for (auto observer : observerList)
        {
            observer->update(v);
        }
    }
    void setPrice(int v)
    {
        price = v;
        notify(v);
    }
};

Observer::Observer(Stock* stock) : stock(stock)
{
    stock->attach(this);
}

Observer::~Observer()
{
    stock->detach(this);
}

int main()
{
    Stock stock;
    Monitor monitor{ &stock };
    Billboard board{ &stock };
    stock.setPrice(10);
    stock.setPrice(20);
}

总结

两个抽象类:

  • Observer

  • Subject

四个接口:

  • Observer

    • update

  • Subject

    • attach

    • detach

    • notify

应用:触发机制、广播通信

0

评论区