欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 旅游 > 设计模式之观察者模式

设计模式之观察者模式

2025/2/23 1:24:11 来源:https://blog.csdn.net/qq_58158950/article/details/143957287  浏览:    关键词:设计模式之观察者模式

背景

假如我们现在有这样一个场景:市场上的股票价格不定时变化,而后台监控者和广告想要实时获取股票信息,我们应该怎么做?

显然在这个场景里,我们有这样一个设计

  • 一个股票类不时更新股票价格
  • 另外有两个类Monitor和BillBoard实时接收股票价格的更新并显示出来

基于以上想法,我们先写出一版代码出来

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;}
};
class Stock {public://实时更新价格,并价格信息反馈给Monitor和BillBoardvoid 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_;
};
void test() {Stock stock;stock.set_price(20);
}

上述代码显然是可以运行的,当Stock修改价格信息时,Monitor和BillBoard可以接受到Stock发送给它的价格信息

但上述代码设计的缺陷很明显

  • Stock类和其余两个类之间的耦合度过高,假如我们还有其他的类也想要实时获取股票信息,那么每次就都得修改Stock类的代码
  • 在实际场景里Stock不可能知道想要获取股票信息的类的方法具体是什么,也就是说printPrice方法和displayPrice对于Stock并不是事先知道的

观察者模式

初步设计

基于上述第二个缺陷,我们可以设计一个虚函数,将Monitor和BillBoard的打印股票信息的方法抽象成一个统计的接口,如下所示

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就知道调用哪个方法来发送股票信息了


class Stock {public:void set_price(int price) {price_ = price;observer->update(price);}private:int price_;Observer * observer;
};

为了能够一次性通知所有订阅者,我们可以使用一个容器保存所有的订阅者,然后遍历发送消息,像这样


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_;
};

当然,想要将消息发送出去,保存消息的容器当然要有订阅者对象才行,因此我们需要设计两个方法用于添加订阅者和撤销订阅者


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_;
};
void test() {Monitor monitor;BillBoard billBoard;Stock stock;//添加订阅者对象stock.attach(&monitor);stock.attach(&billBoard);//修改价格stock.set_price(100);stock.set_price(10000);
}

编译运行

修改

经过上述设计,我们其实已经完成了大部分工作,但是还有一个问题

Stock是发送消息者,但是当订阅消息者每次想要订阅消息时却要Stock将其添加到自己的列表里,这显然是不合理的,这就好比,我想要获取一个公众号的消息,但是必须要先让公众号知道我的存在,然后把我添加进它的列表里才行

对于发送消息者来说,添加的订阅者太多的时候显然是一种负担,而对于订阅者来说,还需要等待发送者将自己添加成功后才能获取到实时消息

因此,我们应该再次修改代码,更换一下主客角色

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); }

如上所示,我们直接修改观察者的代码,为其添加发送方的对象指针,然后调用发送方的方法把自己添加到发送方的列表里,如此:

  • 对于订阅消息的人来说,只需要关心发送方是谁即可
  • 而对于发送方则不需要关心订阅者是谁

完整代码如下

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); }

 我们再来看测试代码


void test() {Stock stock;Monitor monitor(&stock);BillBoard billBoard(&stock);stock.set_price(1);stock.set_price(10);stock.set_price(1000);
}
  • 对于订阅者来说,我关注的是股票信息,因此将股票类的实例对象添加进去
  • 对于发送方来说,我不需要关心谁订阅了我的消息,我只修改股票信息就行

编译运行

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词