你好,这里是codetrend专栏“跟着GPT学设计模式”。
引言
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。
观察者模式(Observer Design Pattern)也被称为发布订阅模式(Publish-Subscribe Design Pattern)。一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫作观察者(Observer)。
不过,在实际的项目开发中,这两种对象的称呼是比较灵活的,有各种不同的叫法,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、EventEmitter-EventListener、Dispatcher-Listener。
在观察者模式中,有两个主要角色:主题(Subject)和观察者(Observer)。主题是被观察的对象,它维护了一个观察者列表,并提供方法用于添加、删除和通知观察者。
观察者是监听主题的对象,它定义了一个更新的方法,在主题状态改变时被调用。
观察者模式的一般工作流程:
- 主题对象维护一个观察者列表,并提供方法用于注册、注销和通知观察者。
- 观察者对象实现一个更新方法,用于在主题状态改变时被调用。
- 当主题的状态发生改变时,它会遍历观察者列表,调用每个观察者的更新方法。
- 观察者根据接收到的通知进行相应的操作,以使自身状态与主题保持一致。
使用 Mermaid 语法类图展示观察者模式的示意图如下:
代码语言:mermaid复制classDiagram
class Subject {
attach(Observer observer)
detach(Observer observer)
notifyObservers()
# observers: Observer[]
}
class Observer {
update()
}
class ConcreteSubject {
- state
getState()
setState(state)
}
class ConcreteObserver {
update()
}
Subject <|-- ConcreteSubject
Subject "1" --> "*" Observer
Observer <|-- ConcreteObserver
ConcreteSubject --> ConcreteObserver
- Subject 是主题类,包含了添加、删除和通知观察者的方法。observers 是一个观察者列表,用于存储注册的观察者对象。
- Observer 是观察者类,定义了一个 update() 方法,用于在主题状态改变时被调用。
- ConcreteSubject 是具体主题类,继承自 Subject,它有一个额外的 state 属性,表示当前状态,并提供了获取和设置状态的方法。
- ConcreteObserver 是具体观察者类,继承自 Observer,实现了 update() 方法,根据主题状态的改变来更新自身状态。
这个类图展示了观察者模式的关键组件及它们之间的关系。主题与观察者之间是一对多的关系,主题可以有多个观察者。
当主题状态改变时,它会通过通知方法 notifyObservers() 来遍历观察者列表,并依次调用每个观察者的 update() 方法,从而实现观察者的更新操作。
观察者模式的使用场景
观察者模式在实际应用中有很多场景,以下是一些观察者模式的应用场景的举例:
- GUI 事件处理:在图形用户界面(GUI)中,观察者模式被广泛应用于事件处理机制。当用户执行某个操作时,比如点击按钮或输入文本,这些操作会触发相应的事件。事件作为主题被通知给注册的观察者,观察者可以根据事件类型做出相应的响应,例如更新界面、执行特定逻辑等。
- 订阅/发布模式:观察者模式也常被称为订阅/发布模式。在发布者-订阅者系统中,发布者充当主题的角色,订阅者则扮演观察者的角色。发布者负责发布消息,订阅者订阅感兴趣的消息类型,并在消息到达时执行相应的操作。这种模式广泛应用于消息队列系统、事件总线等。
- 消息通知系统:观察者模式也可以用于构建消息通知系统。例如,一个新闻平台可以作为主题,用户可以选择订阅感兴趣的新闻类别作为观察者。当新闻发布时,平台会通知所有订阅了该类别的用户,并将新闻推送给他们。
- 游戏开发中的事件管理:在游戏开发中,观察者模式常被用于事件管理。例如,游戏角色之间的互动和协作可以通过观察者模式实现。一个角色可以作为主题,其他角色可以注册为观察者。当主题(例如敌人角色)发生改变时,通知所有观察者(例如友方角色)并执行相应的行为。
实际上观察者模式非常灵活,适用于很多具有发布-订阅关系的场景。它提供了一种松耦合的设计方式,使得主题和观察者能够独立变化,并保持一致性。
观察者模式使用注意事项
- 角色和职责:观察者模式中包括主题(Subject)和观察者(Observer)两个核心角色。主题负责管理观察者的注册、注销以及通知操作,而观察者则定义了接收更新通知并执行相应操作的方法。了解每个角色的职责和关系对于正确使用观察者模式非常重要。
- 主题状态管理:主题在观察者模式中起到承载和管理状态的作用。当主题状态发生变化时,需要通知所有观察者。因此,需要合理设计和管理主题的状态,并及时触发通知操作。
- 多线程安全性:在多线程环境下使用观察者模式时,需要考虑线程安全性。例如,在主题状态变化时,可能会遇到多个线程同时修改主题状态或触发通知的情况。需要采取相应的同步措施或使用线程安全的容器来确保并发访问的正确性。
- 事件传递方式:观察者模式中,主题向观察者传递更新通知的方式可以是同步或异步的。在同步方式下,主题在通知观察者后会等待观察者执行完相应操作,才会继续执行;而在异步方式下,主题通知观察者后立即继续执行,观察者的更新操作在后台进行。了解不同的事件传递方式对系统行为和性能的影响是重要的。
观察者模式编程示例
下面通过一段代码来说明观察者模式的实现。
代码语言:java复制import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update();
}
// 具体主题类
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
// 具体主题类的其他方法
// ...
}
// 具体观察者类
class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("观察者收到更新通知,执行相应操作。");
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
// 创建具体主题对象
ConcreteSubject subject = new ConcreteSubject();
// 创建具体观察者对象
ConcreteObserver observer1 = new ConcreteObserver();
ConcreteObserver observer2 = new ConcreteObserver();
// 注册观察者
subject.attach(observer1);
subject.attach(observer2);
// 主题发生改变时,通知观察者
subject.notifyObservers();
}
}
在上述示例中定义了一个 Subject 接口和一个 Observer 接口,分别表示主题和观察者。
ConcreteSubject 是具体的主题类,实现了 Subject 接口,并包含了维护观察者列表、添加/删除观察者以及通知观察者的方法。
ConcreteObserver 是具体的观察者类,实现了 Observer 接口,并在 update() 方法中定义了观察者接收到更新通知时的操作。
在 main() 方法中创建了具体的主题对象 ConcreteSubject,以及两个具体的观察者对象 observer1 和 observer2。
然后通过 attach() 方法将观察者注册到主题中,然后调用 notifyObservers() 方法通知所有的观察者。当主题发生改变时,所有观察者都会收到更新通知,并执行相应的操作。
以上内容基于 GPT 创建和整理。
关于作者
来自一线全栈程序员nine的探索与实践,持续迭代中。
欢迎关注或者点个赞~