一、什么是观察者模式?
观察者模式又称发布订阅(Publish-Subscribe)模式,顾名思义,这是一种一对多的关系,他可以让多个观察者对象同时监听某一个主题对象,这个主题对象在状态变化时,会通知所有观察者对象,使它们能够自动更新状态。
观察者模式包含以下主要角色:
- Subject:主题,它维护一系列观察者,并提供添加/移除观察者的接口。
- Observer:观察者,它定义一个更新接口,主题更改时更新自身的状态。
- ConcreteSubject:具体主题,将有关状态存入各
ConcreteObserver
对象,并在自身状态改变时发出通知。 - ConcreteObserver:具体观察者,存储一个指向
ConcreteSubject
的引用,接收到通知时更新自身状态。
二、如果不使用观察者模式的情况
如果存在一个这样的业务需求,假设我们要实现一个新闻订阅的系统,用户可以订阅不同的新闻分类,当有新闻发布时,用户可以收到通知。
显然最简单的做法是定义两个类,一个新闻发布类NewsPublisher
,一个用户类User
,当有消息发布时,直接通知用户。
具体实现如下:
// 新闻发布类
class NewsPublisher {
public:
vector<User> users; // 保存每个用户信息
void attach(User user){
observers.push_back(observer); // 加入用户信息
}
void attach(User user){
// 移除用户信息
}
void publishNews(string news) {
// 直接通知每个用户
for (auto user : users) {
user1.receiveNews(news);
}
}
}
// 用户类
class User {
public:
void receiveNews(string news) {
//接收新闻
}
}
int main() {
NewsPublisher publisher;
User user1, user2, user3;
publisher.attach(user1);
publisher.attach(user2);
publisher.attach(user3);
publisher.publishNews("Hello World!");
}
上面这种方法的问题在于:
*NewsPublisher
必须知道所有的User
对象和它的方法,并直接调用它们的方法。这使得系统非常脆弱,一旦新增或者减少User
类,都需要修改NewsPublisher
类。
*User
和NewsPublisher
之间存在强耦合,不满足设计模式的原则。比如这时候我需要另外一种功能的新闻发布类,来实现一些其它的功能,那我是不是还要重新写一个类,再把刚刚的三个基础功能实现一遍,另外新加一个功能,这样代码会特别冗余。
三、使用观察者模式的情况
那该怎么实现User
和NewsPublisher
的松耦合呢,也就是使用观察者模式该怎么写呢?
我们可以把两个类都继承于一个抽象父类,父类写出基本的功能,具体实现如下:
1.定义抽象类News
class News {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
2.定义观察者抽象类Observer
class Observer {
public:
virtual void update() = 0;
};
3.实现具体主题ConcreteNews
class NewsPublisher : public Subject {
private:
list<Observer*> observers;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
// 移除观察者
}
void notify() override {
// 通知所有观察者
}
};
4.实现具体观察者,即用户User
class User : public Observer {
public:
void update() override {
// 更新用户状态
}
};
5.使用
NewsPublisher* publisher = new NewsPublisher();
User* user1 = new User();
User* user2 = new User();
publisher->attach(user1);
publisher->attach(user2);
publisher->notify(); // 通知
该代码通过观察者模式实现NewsPublisher和User之间的松耦合,解决了直接依赖的问题:
-
NewsPublisher不再需要知道所有的User,只需要维护观察者列表,然后notify即可
-
新增User不需要修改NewsPublisher类
-
用户和发布者解耦,可以独立变化。
四、总结
使用观察者模式的优势在于解耦了主题和观察者之间的依赖关系,使得它们可以独立变化。主题不需要知道观察者的具体实现,而观察者也只需关注主题的状态变化而不需要直接与主题类耦合。这样,当需要增加新的观察者或更改通知逻辑时,不会对主题类产生影响。