背景
假如我们现在有这样一个场景:市场上的股票价格不定时变化,而后台监控者和广告想要实时获取股票信息,我们应该怎么做?
显然在这个场景里,我们有这样一个设计
- 一个股票类不时更新股票价格
- 另外有两个类Monitor和BillBoard实时接收股票价格的更新并显示出来
基于以上想法,我们先写出一版代码出来
cpp
class Monitor {
public:
void printPrice(int price) {
std::cout << "monitor price >>> " << price << std::endl;
}
};
class BillBoard {
public:
void displayPrice(int price) {
std::cout << "BillBoard price >>> " << price << std::endl;
}
};
cpp
class Stock {
public:
//实时更新价格,并价格信息反馈给Monitor和BillBoard
void set_price(int price) {
price_ = price;
monitor_->printPrice(price);
billboard_->displayPrice(price);
}
private:
int price_;
std::unique_ptr<Monitor> monitor_;
std::unique_ptr<BillBoard> billboard_;
};
cpp
void test() {
Stock stock;
stock.set_price(20);
}
上述代码显然是可以运行的,当Stock修改价格信息时,Monitor和BillBoard可以接受到Stock发送给它的价格信息
但上述代码设计的缺陷很明显
- Stock类和其余两个类之间的耦合度过高,假如我们还有其他的类也想要实时获取股票信息,那么每次就都得修改Stock类的代码
- 在实际场景里Stock不可能知道想要获取股票信息的类的方法具体是什么,也就是说printPrice方法和displayPrice对于Stock并不是事先知道的
观察者模式
初步设计
基于上述第二个缺陷,我们可以设计一个虚函数,将Monitor和BillBoard的打印股票信息的方法抽象成一个统计的接口,如下所示
cpp
class Observer {
public:
virtual void update(int) = 0;
private:
};
class Monitor : public Observer {
public:
void printPrice(int price) {
std::cout << "monitor >>> " << price << std::endl;
}
void update(int price) override { printPrice(price); }
};
class BillBoard : public Observer {
public:
void displayPrice(int price) {
std::cout << "billoard >>> " << price << std::endl;
}
void update(int price) override { displayPrice(price); }
};
这样,每次新增一个订阅者的时候,Stock就知道调用哪个方法来发送股票信息了
cpp
class Stock {
public:
void set_price(int price) {
price_ = price;
observer->update(price);
}
private:
int price_;
Observer * observer;
};
为了能够一次性通知所有订阅者,我们可以使用一个容器保存所有的订阅者,然后遍历发送消息,像这样
cpp
class Stock {
public:
void notify(int price) {
for (auto &obj : observerlist_) {
obj->update(price);
}
}
void set_price(int price) {
price_ = price;
notify(price);
}
private:
int price_;
std::list<Observer *> observerlist_;
};
当然,想要将消息发送出去,保存消息的容器当然要有订阅者对象才行,因此我们需要设计两个方法用于添加订阅者和撤销订阅者
cpp
class Stock {
public:
//添加订阅者对象
void attach(Observer *obj) { observerlist_.emplace_back(obj); }
//移除订阅者对象
void deattach(Observer *obj) { observerlist_.remove(obj); }
//向所有订阅股票信息的订阅者发送消息
void notify(int price) {
for (auto &obj : observerlist_) {
obj->update(price);
}
}
//修改价格
void set_price(int price) {
price_ = price;
notify(price);
}
private:
int price_;
std::list<Observer *> observerlist_;
};
cpp
void test() {
Monitor monitor;
BillBoard billBoard;
Stock stock;
//添加订阅者对象
stock.attach(&monitor);
stock.attach(&billBoard);
//修改价格
stock.set_price(100);
stock.set_price(10000);
}
编译运行
修改
经过上述设计,我们其实已经完成了大部分工作,但是还有一个问题
Stock是发送消息者,但是当订阅消息者每次想要订阅消息时却要Stock将其添加到自己的列表里,这显然是不合理的,这就好比,我想要获取一个公众号的消息,但是必须要先让公众号知道我的存在,然后把我添加进它的列表里才行
对于发送消息者来说,添加的订阅者太多的时候显然是一种负担,而对于订阅者来说,还需要等待发送者将自己添加成功后才能获取到实时消息
因此,我们应该再次修改代码,更换一下主客角色
cpp
class Stock;
class Observer {
public:
explicit Observer(Stock *stock);
virtual ~Observer();
virtual void update(int) = 0;
protected:
Stock *stock_;
};
Observer::Observer(Stock *stock) : stock_(stock) { stock_->attach(this); }
Observer::~Observer() { stock_->deattach(this); }
如上所示,我们直接修改观察者的代码,为其添加发送方的对象指针,然后调用发送方的方法把自己添加到发送方的列表里,如此:
- 对于订阅消息的人来说,只需要关心发送方是谁即可
- 而对于发送方则不需要关心订阅者是谁
完整代码如下
cpp
class Stock;
class Observer {
public:
explicit Observer(Stock *stock);
virtual ~Observer();
virtual void update(int) = 0;
protected:
Stock *stock_;
};
class Monitor : public Observer {
public:
explicit Monitor(Stock *stock) : Observer(stock) {}
void printPrice(int price) {
std::cout << "monitor >>> " << price << std::endl;
}
void update(int price) override { printPrice(price); }
};
class BillBoard : public Observer {
public:
explicit BillBoard(Stock *stock) : Observer(stock) {}
void displayPrice(int price) {
std::cout << "billoard >>> " << price << std::endl;
}
void update(int price) override { displayPrice(price); }
};
class Stock {
public:
void attach(Observer *obj) { observerlist_.emplace_back(obj); }
void deattach(Observer *obj) { observerlist_.remove(obj); }
void notify(int price) {
for (auto &obj : observerlist_) {
obj->update(price);
}
}
void set_price(int price) {
price_ = price;
notify(price);
}
private:
int price_;
std::list<Observer *> observerlist_;
};
Observer::Observer(Stock *stock) : stock_(stock) { stock_->attach(this); }
Observer::~Observer() { stock_->deattach(this); }
我们再来看测试代码
cpp
void test() {
Stock stock;
Monitor monitor(&stock);
BillBoard billBoard(&stock);
stock.set_price(1);
stock.set_price(10);
stock.set_price(1000);
}
- 对于订阅者来说,我关注的是股票信息,因此将股票类的实例对象添加进去
- 对于发送方来说,我不需要关心谁订阅了我的消息,我只修改股票信息就行
编译运行