初识设计模式——观察者模式(Observer Pattern)

cuixiaogang

观察者模式(Observer Pattern)又被称为发布-订阅模式(Publish-Subscribe Pattern),是一种行为设计模式,它定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。在这个模式里,被观察的对象称为主题(Subject),依赖于主题的对象称为观察者(Observer)

观察者模式的主要构成

1. 抽象主题(Subject)

  • 定义:它是一个接口或抽象类,该角色定义了增加、删除、通知观察者对象的方法。
  • 作用:为具体主题提供了一个统一的接口,使得具体主题能够被观察者观察,并且可以方便地管理观察者列表。

2. 具体主题(Concrete Subject)

  • 定义:抽象主题的具体实现类。它维护一个观察者列表,当自身的状态发生改变时,会调用通知方法来通知所有注册的观察者。
  • 作用:负责存储和管理观察者对象,以及在状态改变时触发通知操作。

3. 抽象观察者(Observer)

  • 定义:它是一个接口或抽象类,定义了一个更新方法,当主题状态发生改变时,该方法会被调用。
  • 作用:为具体观察者提供了一个统一的接口,使得具体观察者能够响应主题的状态变化。

4. 具体观察者(Concrete Observer)

  • 定义:抽象观察者的具体实现类。它实现了抽象观察者定义的更新方法,当接收到主题的通知时,会执行相应的更新操作。
  • 作用:根据主题的状态变化来更新自身的状态或执行特定的操作。

观察者模式的结构图

示例

场景分析

在 Web 开发中,常见的使用观察者模式的案例是事件监听。例如,当用户提交表单时,可能需要触发多个操作,如验证表单数据、记录日志、发送邮件等。这些操作可以作为观察者,而表单提交事件就是主题。

代码

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
<?php
// 定义观察者接口
interface Observer {
public function update($data);
}

// 定义主题接口
interface Subject {
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}

// 具体主题类
class FormSubmitSubject implements Subject {
private $observers = [];
private $formData;

public function attach(Observer $observer) {
$this->observers[] = $observer;
}

public function detach(Observer $observer) {
$key = array_search($observer, $this->observers, true);
if ($key!== false) {
unset($this->observers[$key]);
}
}

public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this->formData);
}
}

public function submitForm($data) {
$this->formData = $data;
$this->notify();
}
}

// 具体观察者类:表单验证
class FormValidator implements Observer {
public function update($data) {
echo "表单验证:数据验证通过,数据为 ". json_encode($data). "<br>";
}
}

// 具体观察者类:日志记录
class Logger implements Observer {
public function update($data) {
echo "日志记录:表单数据已记录,数据为 ". json_encode($data). "<br>";
}
}

// 具体观察者类:邮件发送
class MailSender implements Observer {
public function update($data) {
echo "邮件发送:已发送通知邮件,数据为 ". json_encode($data). "<br>";
}
}

// 使用示例
$formSubject = new FormSubmitSubject();

$validator = new FormValidator();
$logger = new Logger();
$mailSender = new MailSender();

$formSubject->attach($validator);
$formSubject->attach($logger);
$formSubject->attach($mailSender);

$formData = ['username' => 'john_doe', 'email' => 'john@example.com'];
$formSubject->submitForm($formData);

代码含义

在这个示例中,FormSubmitSubject 是主题,当表单提交时,会通知所有的观察者。FormValidator、Logger 和 MailSender 是具体的观察者,它们实现了 Observer 接口,当接收到通知时,会执行相应的操作。

UML类图

UML类图

观察者模式的优缺点

优点

  • 松耦合:主题和观察者之间的耦合度较低,主题不需要知道具体有哪些观察者,只需要维护一个观察者列表,而观察者也不需要知道主题的具体实现细节。
  • 可扩展性:可以方便地添加或删除观察者,而不会影响主题和其他观察者的代码。
  • 支持广播通信:主题状态变化时,会自动通知所有观察者,实现了一种广播机制。

缺点

  • 性能问题:如果观察者数量过多,通知所有观察者可能会导致性能下降。
  • 内存泄漏风险:如果观察者没有正确地从主题中移除,可能会导致内存泄漏。

观察者模式适用的业务场景

  • 事件处理系统:如 GUI 编程中,当用户点击按钮时,会触发一系列的事件处理程序。
  • 状态监控:监控系统资源的使用情况,当资源使用达到一定阈值时,通知相关的监控程序。
  • 消息通知系统:如社交网络中,当用户发布新消息时,通知其所有的粉丝。