观察者模式

观察者模式

由于一些原因,我需要先学观察者模式,其实就是我需要这个模式去进行课设()


感悟

​ 其实所有的设计模式如果只是在看的阶段去过一遍你的感触其实不会很深。相反,如果你在设计中因为一些设计导致出现了一些问题,需要一些设计模式来进行弥补的话,这种学习将会是很深刻的。当然啦,你如果一点设计模式都没有学过,你其实都不会意识到你需要这些设计模式。

​ 所以,在我的设想中,一个设计模式好的学习流程应该是先去系统的学一学一些设计模式,就比如创建型设计模式中的几种,然后去进行使用,在使用的阶段你其实不时就会发现你的创建型设计模式和结构型设计模式通常会出现一些问题。或者吧,也可以先依照设计模式的三原则去进行设计架构,然后你一定会发现使用三原则的话会导致的一些疑惑的。

​ 就比如我的课设,我在进行重构,想要重新规范下三原则中的单一职责原则时,我想要进行数据模块和渲染模块间的解耦,然后发现这种解耦会导致我的渲染操作不知道在哪里调用,这时我就回来看设计模式,发现这种问题可以通过行为模式中的观察者模式来解决,然后我就自我驱动的去学了,这才是一个学习的正反馈过程啊。


存在意图

​ 定义对象建一种一对多的依赖关系,当一个对象的状态发生转变事,所有依赖于它的对象都得到通知并且自动更新。

使用的动机

​ 在实际的设计过程中,我们常常会遇到一个问题。就是当我们对于一个系统进行解耦的时候,我们把它分割为了一系列的模块。就比如,你在原本的设计中设计了一个可以自己实现数据处理和结构渲染的类。现在你要对这个类进行进一步的解耦,我们先不考虑它是怎么解耦的。

​ 我们目前需要看的是,这种解耦会出现的副作用,其中最明显的就是在我们解耦过后,我们原本代码中合作的数据处理模块和渲染模块之间无法协调工作了,这是由于类的特殊性导致的,如果使用嵌套类来实现这只新的模块分割倒也是一种解决方案,但是这种设计又违反了我们的开放封闭原则。所以我们需要一种机制来实现这种独立出来的类的联动。当然,是有其他的一些应用场景的,但是我还没碰到过。

举例

​ 就比如我们的数据模块和渲染模块间的联动,我们期望的是当我们数据发生变动时我们能够实时的去反应出这种变化。这在我们原有的一个系统中只需要去在数据变更时去进行对应的渲染代码执行即可。但是我们现在已经不能够直接去调用渲染代码了,但是我们还是需要这个功能。此时就需要一种机制去进行通知对应的渲染模块去进行工作,而观察者模式恰恰好能够充当这个角色。

​ 说了这么多其实还是实际项目中的应用情况。我们来举几个生活中的例子。

​ 在现实生活中,我们在任何一个视频网站上,通常都有关注这个功能,当我们关注之后,这个博主只要一更新内容,我们如果有对应的设置的话,我们就能收到对应的通知。同时,一个博主可能存在着许多粉丝,相应的,所有这些粉丝都能够收到对应的推送。这就是一种发布-订阅模式。在这种模式中,博主和用户这俩个账号是相对独立的。其中是存在着一种机制来进行实时的通知的。正是由观察着模式来实现这种功能。


简单概括

​ 在观察者模式中,一般存在着俩种角色。一个是订阅者,一个是发布者。一个发布者可能有着多个订阅者。但是一般来说一个订阅者对于发布者是一对一的的。当然实际中,你可能会说一个人可以订阅多个博主,但是这也是不冲突的。这用文字是不好进行阐述的,但是你应该能够理解。

​ 通过进行观察者模式,我们实现了一种通知机制,在发布者发布一些信息之后,所有的订阅者应该都能够收到对应的信息。至于订阅者收到这个信息后会进行一个什么样的处理,这个是观察者模式中不关注的。

​ 在观察者模式中,最重要的就是通知机制。这个会在接下来的UML类图中去进行分析。


UML类图

img

观察者模式的UML类图是需要重点分析的,需要重点理解的就是观察者模式中的通知机制。

​ 这里我们将只进行抽象发布者类和抽象订阅者类的分析,其他的子类都不是很重要。

​ 首先可以看到在途中ConcreteObserver和ConcreteSubject是一个互相聚合的关系。我们就是通过这种互相聚合来实现通知机制的。

ConcreteObserver类

​ 这个类就是我们对应的观察者类。这个设计中使用了一个name作为信息的接收。当然,这个成员也可以是订阅者的名字,不过这个不重要。主要是看到另外的一个成员,一个指针链表,在这个指针链表中,储存的是当前观察者对应的发布者的指针。也就是说,在订阅者的属性中,是存在一个成员用于管理发布者的信息的。其实你应该很可以理解这个存在的意义,就比如你社交软件中关注的博主。这个其实就是你对应的发布者。订阅者可以通过这个list链表去找到其的订阅者信息并以此进行通知操作。

​ 这个其实就是一个对于前面一对多的扩展,实现了一个人可以同时关注多个博主的功能。当然,这里的信息处理事很薄弱的。

​ 还需要来进行着下面的系列成员函数的分析。

addSubject函数

​ 这个函数的作用其实就写在明面上了,就是添加我们的订阅者,通过一个指针去实现对于发布者的添加,实际上就是添加到自己的发布者链表中去进行管理。

deleteSubject函数

​ 这个函数同样很好理解,就是简单的吧我们指定的一个发布者从我们的成员链表中去移除。没什么好说的。

update函数

​ 这个是整个类的核心,用于进行事件的通知,在这UML类图中是体现不出它到底是怎么进行一个通知的,简单来说一下吧,这其实是一个被调用的函数,而且调用对象一般是对应的发布者,发布者能够通过这个接口来进行信息的传递。


ConcreteObsertver类

​ 这个类就是我们的发布者基类了,在这个基类中,很多属性都跟订阅者很相似,我们这里就不加以赘述了。这里主要看到一个函数。就是这里的send函数

send函数

​ 这个函数的作用已经写的很明白了,就是发送信息。它的参数就是它的信息。但是UML类图是体现不出发送这种操作的,还是得等等下的代码分析,不过可以简单的说一下,这里的发布信息就是通过对应的成员链表去进行通知,去进行一次链表的遍历,并依次调用每个成员对应的接收信息的方法去进行推送。


代码实例

​ 在观察者模式中,最重要看的是它的代码,毕竟相对于其他模式,观察者模式在其的UML类图中暴露的信息有限,但是我并不想再进行分析了,我要回去搓我的课设了,所以我要挖一个我可能永远也不会填的坑了。

​ 自己分析去吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>

// 抽象观察者(订阅者)
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string &message) = 0;
};

// 具体观察者(订阅者)
class Subscriber : public Observer {
private:
std::string name;
public:
explicit Subscriber(const std::string &name) : name(name) {}

void update(const std::string &message) override {
std::cout << "Subscriber " << name << " received message: " << message << std::endl;
}

std::string getName() const { return name; } // 用于识别订阅者
};

// 抽象发布者
class Publisher {
public:
virtual ~Publisher() = default;
virtual void subscribe(std::shared_ptr<Observer> observer) = 0;
virtual void unsubscribe(const std::string &observerName) = 0; // 通过标识符移除
virtual void notify(const std::string &message) = 0;
};

// 具体发布者
class NewsPublisher : public Publisher {
private:
std::vector<std::shared_ptr<Observer>> subscribers; // 订阅者列表
public:
void subscribe(std::shared_ptr<Observer> observer) override {
subscribers.push_back(observer);
}

void unsubscribe(const std::string &observerName) override {
subscribers.erase(
std::remove_if(subscribers.begin(), subscribers.end(),
[&observerName](const std::shared_ptr<Observer> &subscriber) {
auto concreteSubscriber = std::dynamic_pointer_cast<Subscriber>(subscriber);
return concreteSubscriber && concreteSubscriber->getName() == observerName;
}),
subscribers.end()
);
}

void notify(const std::string &message) override {
for (const auto &subscriber : subscribers) {
subscriber->update(message);
}
}
};

int main() {
// 创建发布者
auto publisher = std::make_shared<NewsPublisher>();

// 创建订阅者
auto subscriber1 = std::make_shared<Subscriber>("Alice");
auto subscriber2 = std::make_shared<Subscriber>("Bob");

// 订阅新闻
publisher->subscribe(subscriber1);
publisher->subscribe(subscriber2);

// 发布新闻
std::cout << "Publishing news..." << std::endl;
publisher->notify("Breaking News: Observer Pattern in Action!");

// 取消订阅
publisher->unsubscribe("Alice");

// 再次发布新闻
std::cout << "\nPublishing more news..." << std::endl;
publisher->notify("Update: Observer Pattern Still Relevant!");

return 0;
}
-------------本文结束 感谢阅读-------------