在经历了前三天的对象创建型设计模式学习之后,今天我们开始进入行为型设计模式 的探索之旅。行为型模式聚焦于对象之间的通信机制与协作方式 ,其中最经典且应用最广泛的就是------观察者模式(Observer Pattern)。本文将用8000字篇幅,从设计哲学、模式原理、多语言实现到企业级应用,全方位解析这个"事件驱动编程基石"。
一、设计模式学习全景回顾
📜 Day 1~3 核心要点提炼
Day | 模式 | 核心思想 | 典型应用场景 | 实现难点 |
---|---|---|---|---|
1 | 单例模式 | 全局唯一实例访问控制 | 日志系统、配置中心 | 线程安全与双重检查锁定 |
2 | 工厂方法模式 | 将对象创建延迟到子类工厂 | 跨数据库驱动兼容 | 开闭原则的实践 |
3 | 抽象工厂模式 | 创建相关/依赖对象族 | 跨平台UI组件库 | 产品族扩展复杂度 |
知识链提示 :前三天我们解决了对象创建 的问题,今天开始处理对象行为的协调问题。
二、观察者模式深度剖析
📌 模式定义与本质
官方定义(GoF):
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
中文释义:
在对象间建立一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
设计哲学:
- 松耦合原则:主题与观察者之间通过抽象接口交互
- 开闭原则:新增观察者无需修改主题代码
- 事件驱动架构:状态变化触发连锁反应
🌍 现实世界映射模型
场景1:新闻出版系统
- 出版社(Subject)发行新期刊
- 订阅用户(Observer)自动收到纸质刊物
- 新增订阅用户只需登记,无需修改出版社印刷流程
场景2:智能家居系统
- 温控传感器(Subject)检测到温度变化
- 空调、加湿器、手机App(Observers)同步响应
- 新设备接入只需注册监听,不影响传感器核心逻辑
场景3:电商库存预警
- 商品库存(Subject)数量变化
- 采购系统、营销系统、物流系统(Observers)触发对应操作
- 各系统独立升级不影响库存监控机制

三、模式结构全景透视
🧩 UML类图详解
plantuml
@startuml
interface Subject {
+ attach(Observer)
+ detach(Observer)
+ notify()
}
interface Observer {
+ update()
}
class ConcreteSubject {
- state: int
+ getState(): int
+ setState(int)
}
class ConcreteObserverA {
- subject: Subject
+ update()
}
class ConcreteObserverB {
- subject: Subject
+ update()
}
Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserverA
Observer <|-- ConcreteObserverB
ConcreteSubject o-- Observer : observers
ConcreteObserverA --> ConcreteSubject : subject
ConcreteObserverB --> ConcreteSubject : subject
@enduml
角色解析:
组件 | 职责说明 |
---|---|
Subject(抽象主题) | 定义添加、删除、通知观察者的接口 |
ConcreteSubject | 维护具体状态,状态变更时触发通知 |
Observer | 定义更新接口,用于接收主题通知 |
ConcreteObserver | 实现更新逻辑,可保存指向具体主题的引用以同步状态 |
⚙️ 工作原理时序图
plantuml
@startuml
participant Client
participant ConcreteSubject
participant Observer1
participant Observer2
Client -> ConcreteSubject: setState(newState)
ConcreteSubject -> ConcreteSubject: state = newState
ConcreteSubject -> ConcreteSubject: notify()
ConcreteSubject -> Observer1: update()
Observer1 -> ConcreteSubject: getState()
ConcreteSubject --> Observer1: currentState
ConcreteSubject -> Observer2: update()
Observer2 -> ConcreteSubject: getState()
ConcreteSubject --> Observer2: currentState
@enduml
四、多语言实现示例
🐍 Python 实现:气象站预警系统
python
from abc import ABC, abstractmethod
from typing import List
class WeatherStation:
"""具体主题:气象站"""
def __init__(self):
self._observers: List[WeatherObserver] = []
self._temperature = 0.0
def attach(self, observer: 'WeatherObserver') -> None:
self._observers.append(observer)
def detach(self, observer: 'WeatherObserver') -> None:
self._observers.remove(observer)
def notify(self) -> None:
for observer in self._observers:
observer.update(self)
@property
def temperature(self) -> float:
return self._temperature
@temperature.setter
def temperature(self, value: float) -> None:
self._temperature = value
self.notify()
class WeatherObserver(ABC):
"""抽象观察者"""
@abstractmethod
def update(self, subject: WeatherStation) -> None:
pass
class MobileApp(WeatherObserver):
"""具体观察者:手机应用"""
def update(self, subject: WeatherStation) -> None:
print(f"Mobile App: 当前温度已更新至 {subject.temperature}°C")
class TVDisplay(WeatherObserver):
"""具体观察者:电视显示"""
def update(self, subject: WeatherStation) -> None:
print(f"TV Display: 温度变化提醒 → {subject.temperature}摄氏度")
# 客户端调用
if __name__ == "__main__":
station = WeatherStation()
mobile = MobileApp()
tv = TVDisplay()
station.attach(mobile)
station.attach(tv)
station.temperature = 25.5
station.temperature = 28.0
station.detach(tv)
station.temperature = 30.2
输出结果:
Mobile App: 当前温度已更新至 25.5°C
TV Display: 温度变化提醒 → 25.5摄氏度
Mobile App: 当前温度已更新至 28.0°C
TV Display: 温度变化提醒 → 28.0摄氏度
Mobile App: 当前温度已更新至 30.2°C
☕ Java 实现:拍卖竞价系统
java
import java.util.ArrayList;
import java.util.List;
interface AuctionListener {
void onBidAccepted(double price);
}
class AuctionHouse {
private final List<AuctionListener> listeners = new ArrayList<>();
private double currentBid = 0.0;
public void addListener(AuctionListener listener) {
listeners.add(listener);
}
public void removeListener(AuctionListener listener) {
listeners.remove(listener);
}
public void placeBid(double newBid) {
if (newBid > currentBid) {
currentBid = newBid;
notifyListeners();
}
}
private void notifyListeners() {
for (AuctionListener listener : listeners) {
listener.onBidAccepted(currentBid);
}
}
}
class Bidder implements AuctionListener {
private final String name;
public Bidder(String name) {
this.name = name;
}
@Override
public void onBidAccepted(double price) {
System.out.printf("【%s】收到最新报价:%.2f元%n", name, price);
}
}
public class AuctionSystem {
public static void main(String[] args) {
AuctionHouse house = new AuctionHouse();
Bidder bob = new Bidder("Bob");
Bidder alice = new Bidder("Alice");
house.addListener(bob);
house.addListener(alice);
house.placeBid(1000);
house.placeBid(1500);
house.removeListener(alice);
house.placeBid(2000);
}
}
运行结果:
【Bob】收到最新报价:1000.00元
【Alice】收到最新报价:1000.00元
【Bob】收到最新报价:1500.00元
【Alice】收到最新报价:1500.00元
【Bob】收到最新报价:2000.00元
五、模式变体与进阶应用
1. 推模型 vs 拉模型
类型 | 数据传递方式 | 优点 | 缺点 |
---|---|---|---|
推模型 | 主题将详细数据通过update参数推送 | 观察者无需主动请求数据 | 可能传递不需要的数据 |
拉模型 | 观察者从主题主动拉取所需数据 | 按需获取,减少数据传输量 | 增加主题与观察者的耦合度 |
推模型实现示例:
cpp
// 主题接口
class StockSubject {
public:
virtual void notifyObservers(const std::string& symbol, float price) = 0;
};
// 观察者接口
class StockObserver {
public:
virtual void onPriceChanged(const std::string& symbol, float price) = 0;
};
拉模型实现示例:
cpp
class StockSubject {
public:
virtual float getPrice(const std::string& symbol) const = 0;
};
class StockObserver {
public:
virtual void update(StockSubject* subject) = 0;
};
2. 线程安全观察者模式
多线程环境下的挑战:
- 观察者注册/注销时的竞态条件
- 通知过程中观察者列表的修改
- 观察者处理通知的线程阻塞
解决方案:
java
public class ConcurrentSubject {
private final CopyOnWriteArrayList<Observer> observers =
new CopyOnWriteArrayList<>();
public void addObserver(Observer o) {
observers.addIfAbsent(o);
}
public void notifyObservers() {
for (Observer o : observers) {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> o.update(this));
}
}
}
3. 基于事件总线的全局观察者
架构示意图:
+---------------+ Event +-----------------+
| Publisher |------------->| Event Bus |
+---------------+ +-----------------+
| ^
| |
v |
+-----------------+
| Subscriber |
+-----------------+
TypeScript实现:
typescript
type Handler<T> = (event: T) => void;
class EventBus {
private handlers = new Map<string, Handler<any>[]>();
on<T>(eventType: string, handler: Handler<T>) {
const handlers = this.handlers.get(eventType) || [];
handlers.push(handler);
this.handlers.set(eventType, handlers);
}
emit<T>(eventType: string, event: T) {
const handlers = this.handlers.get(eventType);
handlers?.forEach(h => h(event));
}
}
// 使用示例
const bus = new EventBus();
bus.on('auction:bid', (price: number) => {
console.log(`New bid: $${price}`);
});
bus.emit('auction:bid', 2500);
六、企业级应用案例分析
案例1:Spring Framework事件机制
核心组件:
ApplicationEvent
:所有应用事件的基类ApplicationListener
:观察者接口ApplicationEventPublisher
:主题角色
典型流程:
- 定义自定义事件:
java
public class OrderCreatedEvent extends ApplicationEvent {
public OrderCreatedEvent(Order source) {
super(source);
}
}
- 发布事件:
java
@Autowired
ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// ...业务逻辑
publisher.publishEvent(new OrderCreatedEvent(order));
}
- 监听事件:
java
@Component
public class InventoryUpdateListener
implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
updateInventory(event.getOrder());
}
}
案例2:Node.js EventEmitter
核心机制:
javascript
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const emitter = new MyEmitter();
// 添加观察者
emitter.on('data', (chunk) => {
console.log(`Received chunk: ${chunk}`);
});
// 触发事件
setInterval(() => {
emitter.emit('data', Date.now());
}, 1000);
高级特性:
- 一次性监听器:
emitter.once()
- 错误处理:
error
特殊事件 - 最大监听数限制:
emitter.setMaxListeners()
七、模式对比与选型指南
观察者模式 vs 发布订阅模式
维度 | 观察者模式 | 发布订阅模式 |
---|---|---|
耦合度 | 主题直接持有观察者引用 | 通过中间渠道完全解耦 |
通信方式 | 同步调用 | 通常异步消息队列 |
扩展性 | 适合简单场景 | 支持复杂路由和过滤 |
典型实现 | Java Observable | RabbitMQ、Kafka |
性能 | 高(直接调用) | 依赖中间件性能 |
适用场景 | 单进程内部通信 | 分布式系统、跨服务通信 |
与其他行为模式的关系
- 与中介者模式:都处理对象间通信,但中介者集中管理交互
- 与责任链模式:观察者广播通知,责任链顺序传递请求
- 与策略模式:观察者改变对象间交互方式,策略改变算法
八、今日练习题与详解
题目一:优化股票监控系统(进阶)
需求背景 :
现有股票监控系统通知效率较低,需要实现:
- 支持按股票代码精确订阅
- 允许设置价格波动阈值(如TSLA波动超过5%才通知)
- 实现观察者优先级机制
参考实现:
python
class SmartStockServer:
def __init__(self):
self._subscriptions = defaultdict(dict) # {symbol: {observer: (threshold, priority)}}
def subscribe(self, symbol, observer, threshold=0, priority=0):
self._subscriptions[symbol][observer] = (threshold, priority)
def unsubscribe(self, symbol, observer):
del self._subscriptions[symbol][observer]
def update_price(self, symbol, new_price):
observers = self._subscriptions.get(symbol, {})
sorted_observers = sorted(
observers.items(),
key=lambda x: -x[1][1] # 按优先级降序
)
for observer, (threshold, _) in sorted_observers:
old_price = observer.last_prices.get(symbol, new_price)
change = abs(new_price - old_price) / old_price
if change >= threshold:
observer.update(symbol, new_price)
题目二:分布式观察者模式设计
场景需求 :
设计一个跨微服务的订单状态变更通知系统,要求:
- 服务间完全解耦
- 支持动态增减订阅方
- 保证消息可靠性
架构方案:
- 使用 Kafka 作为消息中间件
- 定义统一事件协议(Protobuf/JSON Schema)
- 每个微服务作为独立Consumer Group
- 实现幂等处理防止重复消费
事件示例:
protobuf
message OrderEvent {
string event_id = 1;
string order_id = 2;
enum EventType {
CREATED = 0;
PAID = 1;
SHIPPED = 2;
COMPLETED = 3;
}
EventType type = 3;
google.protobuf.Timestamp occurred_at = 4;
map<string, string> metadata = 5;
}
九、总结与展望
观察者模式关键点回顾
- 核心价值:建立松耦合的对象间动态依赖关系
- 实现要点 :
- 定义清晰的Subject-Observer接口
- 管理观察者注册机制
- 合理选择推/拉模型
- 适用场景 :
- 事件驱动架构
- 跨模块状态同步
- 需要广播通知的场合
行业应用趋势
- 响应式编程:RxJS、Reactor等框架的观察者模式扩展
- Serverless架构:通过事件触发器实现函数调度
- 物联网领域:设备状态变更的实时同步
明日预告:策略模式(Strategy Pattern)------ 算法即对象,动态切换业务逻辑的核心技术。我们将深入探讨:
- 如何消除复杂的条件分支语句
- 支付系统多通道选择的最佳实践
- 与工厂模式的联合应用技巧
本文从基础到进阶,全面解析了观察者模式的设计哲学与实践应用。建议读者结合GitHub上的示例代码进行实战演练,同时思考如何在现有项目中应用该模式解决实际耦合问题。设计模式的学习需要理论与实践并重,才能真正内化为架构设计能力。