设计模式:观察者模式
在开发一个电商系统时,用户下单成功后需要做三件事:扣减库存、发送短信通知、记录日志。最简单的实现方法是把这三步逻辑直接写在下单方法里:
java
public void createOrder(Order order) {
// 创建订单
orderDao.save(order);
// 扣减库存
inventoryService.deduct(order.getProductId());
// 发送短信
smsService.send(order.getUserId(), "下单成功");
// 记录日志
logService.info("订单创建: " + order.getId());
}
功能是实现了,但问题是:如果以后还要加"发送邮件通知"、"更新用户积分"等等需求呢?每加一个需求,就得改一次 createOrder 方法。这个方法会越来越臃肿,而且下单逻辑和这些后续操作完全耦合在一起。
对于这个问题,我们就可以使用观察者模式来优化。
观察者模式是什么
观察者模式定义了一种一对多的依赖关系:当一个对象(被观察者)状态发生变化时,所有依赖它的对象(观察者)都会自动收到通知。
基本结构
观察者模式的核心角色只有两个:

在观察者模式中,创建订单是被观察者,扣减库存、发送短信等连带操作是观察者。每个观察者注册到被观察者上,当订单创建成功时,被观察者通知所有已注册的观察者,观察者收到通知后各自执行对应的操作。
实现步骤
第一步:定义观察者接口
所有观察者都要实现同一个接口,这样被观察者才能统一通知:
java
public interface Observer {
void update(String event, Object data);
}
update 方法接收两个参数:事件类型和事件数据,观察者根据这两个参数决定做什么。
第二步:定义被观察者(事件中心)
被观察者需要维护观察者列表、提供注册方法、状态变化时通知所有观察者:
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EventCenter {
private Map<String, List<Observer>> listeners = new HashMap<>();
public void subscribe(String event, Observer observer) {
if (!listeners.containsKey(event)) {
listeners.put(event, new ArrayList<>());
}
listeners.get(event).add(observer);
}
public void unsubscribe(String event, Observer observer) {
List<Observer> list = listeners.get(event);
if (list != null) {
list.remove(observer);
}
}
public void publish(String event, Object data) {
List<Observer> list = listeners.get(event);
if (list != null) {
for (Observer observer : list) {
observer.update(event, data);
}
}
}
}
如果事件还没有注册过观察者,就自动创建一个空列表,调用方不需要关心初始化问题。
第三步:实现具体观察者
java
// 库存扣减观察者
public class InventoryObserver implements Observer {
@Override
public void update(String event, Object data) {
Order order = (Order) data;
System.out.println("扣减库存: 商品" + order.getProductId() + ", 数量" + order.getQuantity());
}
}
// 短信通知观察者
public class SmsObserver implements Observer {
@Override
public void update(String event, Object data) {
Order order = (Order) data;
System.out.println("发送短信: 用户" + order.getUserId() + "下单成功");
}
}
// 日志记录观察者
public class LogObserver implements Observer {
@Override
public void update(String event, Object data) {
Order order = (Order) data;
System.out.println("记录日志: 订单" + order.getId() + "已创建");
}
}
每个观察者只关心自己的逻辑,不知道其他观察者的存在。
第四步:在业务中使用
java
public class OrderService {
private EventCenter eventCenter;
public OrderService(EventCenter eventCenter) {
this.eventCenter = eventCenter;
}
public void createOrder(Order order) {
orderDao.save(order);
eventCenter.publish("order.created", order);
}
}
启动时注册观察者:
java
EventCenter eventCenter = new EventCenter();
eventCenter.subscribe("order.created", new InventoryObserver());
eventCenter.subscribe("order.created", new SmsObserver());
eventCenter.subscribe("order.created", new LogObserver());
OrderService orderService = new OrderService(eventCenter);
orderService.createOrder(new Order("001", "P001", 2, "U123"));
输出:
扣减库存: 商品P001, 数量2
发送短信: 用户U123下单成功
记录日志: 订单001已创建
完整执行流程

下单服务和后续操作完全解耦。需要加新功能时,写一个 Observer 实现类注册一下就行,不用改任何已有代码。
观察者模式 vs 直接调用
| 对比项 | 直接调用 | 观察者模式 |
|---|---|---|
| 耦合度 | 下单服务需要知道所有后续操作 | 下单服务只发布事件,不知道谁在监听 |
| 扩展性 | 新增操作需改下单服务代码 | 新增操作只需注册新观察者 |
| 可测试性 | 难以单独测试下单逻辑 | 可以单独测试下单逻辑,观察者独立测试 |
| 执行顺序 | 代码顺序固定 | 观察者执行顺序取决于注册顺序 |
| 复杂度 | 简单直接 | 引入了间接层,稍微复杂 |
消息队列:RabbitMQ、Kafka 这些消息中间件,本质上也是观察者模式的分布式实现。生产者发布消息,消费者订阅并处理。
小结
观察者模式的核心:发布-订阅,事件驱动。被观察者只管发布事件,观察者自己决定要不要监听、监听后做什么。两者通过事件连接,互不依赖。
这个模式解决的是"当A发生变化时,B、C、D需要做出反应"这类问题。如果代码里到处都是"做完A之后紧接着做B、C、D",而且B、C、D还在不断膨胀,就是观察者模式该出场的时候。