资料来源:
示例
某一系统能够根据股票价格实时更新电子广告牌和显示器的信息。描述很简单,我们可以简易的写一个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
应用:触发机制、广播通信
评论区