常用知名购物网站,东莞市官网网站建设平台,网站开发高级工程师,重庆是哪个省哪个市本文内容来源于B站#xff1a; 【「观察者模式」与「发布/订阅模式」#xff0c;你分得清楚吗#xff1f;】 文章目录 观察者模式#xff08;Observer Pattern#xff09;的代码优化观察者模式 与 发布订阅模式 他们是一样的吗#xff1f;发布订阅模式总结 我们想象这样一…本文内容来源于B站 【「观察者模式」与「发布/订阅模式」你分得清楚吗】 文章目录 观察者模式Observer Pattern的代码优化观察者模式 与 发布订阅模式 他们是一样的吗发布订阅模式总结 我们想象这样一个场景 我们需要开发一个软件系统该系统可以实时接收某支股票的最新价格系统可以控制将这个价格展示在路边的电子广告牌上同时坐在控制室中的管理人员可以在显示器上看到这个价格。 当每支股票价格发生变化时电子广告牌和显示器上的股票价格也要实时变化。 我们首先使用C来模拟他的一种可能的实现方式 引入必要的头文件创建一个 Monitor 类来表示显示器其中包含 print 方法来更新显示器的显示内容创建一个 Billboard 类来表示广告牌同样已有 print 方法来进行更新创建 Stock 类该类封装了与股票相关的数据和操作它包含有一个表示当前股票价格的成员变量 price以及一个用于更改其值的成员方法 setPrice。既然 Stock 类是通过 setPrice 来更新股票价格的那么我们也可以在这里调用 Monitor 和 Billboard 类中的接口来同步更新广告牌和显示器上的数据所以我们在 Stock 类找那个加入这两个类中的指针来实现接口的调用。需要补充的是我们需要在 Stock 类中初始化这里的两个类指针让他们指向已经创建好的 Monitor 和 Billboard 类对象。 #include iostream
struct Monitor {void print(int v) {std::cout Monitor v;}
};
struct Billboard {void display(int v) {std::cout Billboard v;}
};
struct Stock {int price 20;Monitor* monitor;Billboard* billboard;Stock(Monitor* monitor, Billboard* billboard): monitor(monitor), billboard(billboard)void setPrice(int v) {price v;monitor-print(price);billboard-print(price);}
};随后我们就可以调用 Stock 对象上的 setPrice 方法来更新股票价格并且广告牌与显示器上的数字也会被相应更新。
int main () {Monitor monitor;Billboard billboard;Stock stock {monitor, billboard};stock.setPrice(10);
}但是这样的实现方式有许多的问题
类之间的紧耦合Stock 类的稳定性依赖于 Monitor 与 Billboard 类接口的稳定而当这些接口的名称或使用方式发生变化时那么 Stock 类就需要被同时修改并且同样的情况也发生在有更多的显示媒介加入时比如软件希望同时支持在手机APP上显示最新的股票价格这个时候我们也需要再次修改 Stock 类。这样的紧耦合使得代码的维护成本变得很高那么如何解决呢答案就是观察者模式
观察者模式Observer Pattern的代码优化
也就是我们的 Monitor 类和 Billboard 类都是观察者这里我们可以提供一个统一的父类 Observer 并且在里面写入 update 接口这个接口将所有观察者在观测时发生的不同动作进行了统一的包装。Observer 的构造和析构会在后面进行补全
#include iostream
struct Observer {Observer();virtual ~Observer();virtual void update(int) 0;
}
struct Monitor {void print(int v) {std::cout Monitor v;}
};
struct Billboard {void display(int v) {std::cout Billboard v;}
};然后我们的 Monitor 类的构造函数也做相应的修改然后我们在 Monitor 类中实现父类的 update 接口Billboard 观察者类也同理所有观察者都需要将当观测数据发生变化时可能发生的行为封装在统一的 update(int) 接口中。 struct Monitor {Monitor() : Observer() {}void print(int v) {std::cout Monitor v;}void update(int v) override {print(v);}
};
struct Billboard {Billboard() : Observer() {}void display(int v) {std::cout Billboard v;}void update(int v) override {display(v);}
};下面我们把 Stock 类也加入到代码中我们首先为他提供一个前置声明。接着我们在 Observer 类中加入一个指向 Stock 类对象的指针引用随后我们改写 Monitor 和 Billboard 类的构造函数接着我们将之前那段 Stock 类的定义原封不动得放入到代码中。
#include iostreamstruct Stock;struct Observer {Stock* stock;Observer(Stock* stock);virtual ~Observer();virtual void update(int) 0;
};
struct Monitor {Monitor(Stock* stock) : Observer(stock) {}void print(int v) {std::cout Monitor v;}
};
struct Billboard {Billboard(Stock* stock) : Observer(stock) {}void display(int v) {std::cout Billboard v;}
};struct Stock {int price 0;void setPrice(int v) {price v;}
};在观察者模式下 Stock 类作为所有观察者关注的对象它需要支持三个成员方法notify(int) detach(Observer*) attach(Observer*)这三个方法会围绕 Stock 类内部维护的一个集合进行操作。这个集合中就存放有指向 Oberser 子类对象的指针。这些指针指向的就是所有对 Stock 类感兴趣的观察者对象。 #include list
struct Stock {int price 0;std::listObserver* observerList;void attach(Observer* o) {observerList.push_back(0);}void detach(Observer* o) {observerList.remove(0);}void setPrice(int v) {price v;}
};这里我们的 notify(int v) 方法是观察者模式的核心这个方法会遍历观察者指针容器并以此调用每个观察者对象上的 update 方法。通过这种方式Stock 类便能够在状态发生变化时及时通知所有对此感兴趣的观察者对象来进行相应的更新操作最后我们在 setPrice 方法中调用 notify 方法这样在股价发生变化时 能够通知到所有观察者对象。
#include list
struct Stock {int price 0;std::listObserver* observerList;void attach(Observer* o) {observerList.push_back(0);}void detach(Observer* o) {observerList.remove(0);}void notify(int v) {for (auto observer : observerList) {observer-update(v);}}void setPrice(int v) {price v;notify(v);}
};下一步我们需要补全 Observer 类的构造函数让每一个观察者对象在完成构造后都能被加入到某个 Stock 对象的观察者集合中。而当观察者对象被析构时则从相应的观察者集合中被移除。
Observer::observer(Stock* stk) : stock(stk) {stock-attach(this);
}
Observer::observer(Stock* stk) : stock(stk) {stock-detach(this);
}最后在 main 函数中我们可以这样来使用这些类
int main () {Stock stock;Monitor monitor { stock };Billboard board { stock };stock.setPrice(10);
}我们可以看到当 Stock 类在构造时不再依赖任何观察者对象因此它的实现可以保持稳定。而观察者对象也可以自由选择观察目标可以是不同的 Stock 对象同时不同种类的观察者对象的横向扩展也变得更加灵活。 UML类图如上并且在某些情况下我们可以进一步对观察者模式进行抽象。从而得到另一个版本的 UML 类图和另一个版本的代码实现。 我们为 Stock 类提供了独立的抽象接口 Subject 从而将 attach、detach 以及 notify 这三个标准接口抽离出来。而 Stock 则保有自己的内部状态为了保证状态访问的合法性他们仅能够通过专有的访问器进行访问下面是对应的 C 代码实现
#include iostream
#include liststruct Observer;
struct Subject {std::listObserver* observerList;virtual void attach(Observer* o) 0;virtual void detach(Observer* o) 0;virtual void notify() 0;
};class Stock : public Subject {int price 0;public:int getPrice();void setPrice(int);void attach(Observer* o) override;void detach(Observer* o) override;void notify() override;
};struct Observer {Subject* sub;Observer(Subject* sub);virtual ~Observer();virtual void update() 0;
};struct Monitor : Observer {Monitor(Subject* sub) : Observer(sub) {}void print(int v) const { std::cout Monitor: v std::endl; }void update() override { print(static_castStock*(sub)-getPrice()); }
};struct Billboard : Observer {Billboard(Stock* stock) : Observer(stock) {}void display(int v) const { std::cout Billboard: v std::endl; }void update() override { display(static_castStock*(sub)-getPrice()); }
};int Stock::getPrice(void) { return price; }
void Stock::setPrice(int v) {price v;notify();
}
void Stock::attach(Observer* o) { observerList.push_back(o); }
void Stock::detach(Observer* o) { observerList.remove(0); }
void Stock::notify() {for (auto observer : observerList) {observer-update();}
}Observer::Observer(Subject* sub) : sub(sub) { sub-attach(this); }Observer::~Observer() { sub-detach(this); }int main() {Stock stock;Monitor monitor{stock};Billboard board{stock};stock.setPrice(10);
}观察者模式 与 发布订阅模式 他们是一样的吗
发布订阅模式
发布订阅模式是一种常用的系统架构模式他用来规定一个系统中的不同部分之间应该如何进行消息传递。
在这个模式中发布者可以是系统中的消息服务、事件服务而订阅者可以是系统中的其他服务比如网关服务、路由服务等等。
发布者与订阅者之间互相不知道对方的存在他们之间通过名为消息代理的部分连接起来。发布者会向消息代理发送不同类型的消息比如一个系统通知、一个发生的事件。而订阅者则会与消息代理通信选择自己想要订阅的消息类型。
在之后的流程中每当消息代理接收到符合要求的消息便会把他们直接转发给相应的订阅者进行处理这便是发布订阅模式的基本形式。 总结
所以综合来看我们可以得出这样的结论