1. 观察者模式实战解析
观察者模式是我在开发中最常用的设计模式之一,特别是在处理事件驱动系统时。记得去年做电商促销系统时,商品价格变化需要实时通知库存管理、营销活动和用户端,观察者模式帮我们优雅地解决了这个问题。
这个模式的核心在于建立一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,所有依赖它的观察者都会自动收到通知并更新。就像微信群里的消息通知——当群主(主题)发送新消息时,所有群成员(观察者)都会立即收到提醒。
关键提示:观察者模式特别适合处理对象间松耦合的通信场景,当需要确保一个对象的变更能自动通知其他相关对象时,这应该是你的首选方案。
1.1 模式结构与核心组件
典型的观察者模式包含四个关键角色:
-
Subject(主题):维护观察者列表,提供注册和删除观察者的方法。就像微信公众号平台,管理着所有订阅用户。
-
Observer(观察者):定义更新接口,通常是一个抽象类或接口。相当于微信用户需要实现的"接收消息"能力。
-
ConcreteSubject(具体主题):实现Subject接口,存储具体状态,状态改变时通知所有观察者。比如某个具体的公众号。
-
ConcreteObserver(具体观察者):实现Observer接口,维护对具体主题的引用,存储与主题相关的状态。就像你手机上安装的微信客户端。
java复制// 主题接口
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 观察者接口
public interface Observer {
void update(float temperature, float humidity, float pressure);
}
// 具体主题
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
// 实现主题接口方法...
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
// 具体观察者
public class CurrentConditionsDisplay implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("当前天气: " + temperature + "°C 和 " + humidity + "% 湿度");
}
}
1.2 推模型 vs 拉模型
观察者模式有两种主要实现方式,各有适用场景:
推模型(Push Model):
- 主题主动将详细数据推送给观察者
- 观察者的update方法包含完整的数据参数
- 适合观察者总是需要全部数据的场景
- 可能造成不必要的数据传输
拉模型(Pull Model):
- 主题只通知观察者数据已变更
- 观察者根据需要从主题拉取特定数据
- 主题需要提供getter方法供观察者查询
- 更灵活,减少不必要的数据传输
java复制// 推模型示例
public interface PushObserver {
void update(float temp, float humidity, float pressure);
}
// 拉模型示例
public interface PullObserver {
void update();
}
public class PullWeatherData implements Subject {
// ...其他代码相同
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(); // 只通知,不传数据
}
}
public float getTemperature() {
return temperature;
}
// 其他getter方法...
}
在实际项目中,我通常根据以下原则选择模型:
- 如果观察者总是需要全部数据,用推模型更直接
- 如果观察者需要的数据各不相同,拉模型更合适
- 网络传输场景下,考虑数据量选择更经济的方案
2. 观察者模式的高级应用
2.1 异步观察者实现
在真实项目特别是分布式系统中,同步通知可能造成性能瓶颈。这时我们需要异步观察者模式:
java复制public class AsyncWeatherData extends WeatherData {
private ExecutorService executor;
public AsyncWeatherData() {
executor = Executors.newCachedThreadPool();
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
executor.execute(() -> {
observer.update(temperature, humidity, pressure);
});
}
}
public void shutdown() {
executor.shutdown();
}
}
实战经验:异步实现时要注意线程安全和观察者执行顺序问题。我曾遇到过一个bug,因为观察者之间有依赖关系,异步通知导致执行顺序错乱。解决方案是使用有界队列和优先级执行器。
2.2 防止观察者内存泄漏
观察者模式常见的一个陷阱是内存泄漏。当观察者没有正确注销时,主题会持有观察者的引用,阻止垃圾回收:
java复制// 错误示例:观察者没有注销
public class LeakyObserver implements Observer {
private Subject subject;
public LeakyObserver(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
}
// 忘记实现注销逻辑...
}
// 正确做法:提供注销方法
public class SafeObserver implements Observer {
private Subject subject;
public SafeObserver(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
}
public void unregister() {
subject.removeObserver(this);
}
// ...其他实现
}
最佳实践:
- 在观察者生命周期结束时主动注销
- 使用WeakReference实现自动注销
- 对于Android开发,特别注意Activity作为观察者时的生命周期管理
2.3 观察者模式与事件总线
在大规模系统中,直接使用观察者模式可能导致复杂的依赖关系。这时可以引入事件总线(Event Bus)作为中介:
| 特性 | 经典观察者 | 事件总线 |
|---|---|---|
| 耦合度 | 主题知道观察者 | 完全解耦 |
| 灵活性 | 较低 | 高 |
| 性能 | 通常更高 | 可能较低 |
| 适用场景 | 直接明确的关系 | 系统级事件 |
java复制// 简单事件总线实现
public class EventBus {
private Map<Class<?>, List<Consumer<?>>> handlers = new ConcurrentHashMap<>();
public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
@SuppressWarnings("unchecked")
public <T> void publish(T event) {
List<Consumer<?>> eventHandlers = handlers.get(event.getClass());
if (eventHandlers != null) {
eventHandlers.forEach(handler -> ((Consumer<T>) handler).accept(event));
}
}
}
// 使用示例
EventBus bus = new EventBus();
bus.subscribe(PriceChangeEvent.class, event -> {
System.out.println("价格变化处理: " + event.getNewPrice());
});
3. 实战案例:电商价格监控系统
3.1 需求分析
假设我们需要为电商平台实现一个价格监控系统:
- 当商品价格变化时,需要通知:
- 库存系统更新缓存
- 促销系统检查是否触发促销规则
- 用户通知系统向收藏该商品的用户发送提醒
- 数据分析系统记录价格变化
3.2 类设计
java复制// 价格事件类
public class PriceChangeEvent {
private String productId;
private BigDecimal oldPrice;
private BigDecimal newPrice;
private Instant changeTime;
// 构造器、getter方法...
}
// 主题接口
public interface PriceObservable {
void addObserver(PriceObserver observer);
void removeObserver(PriceObserver observer);
void notifyObservers(PriceChangeEvent event);
}
// 观察者接口
public interface PriceObserver {
void onPriceChanged(PriceChangeEvent event);
}
// 具体商品价格主题
public class ProductPrice implements PriceObservable {
private List<PriceObserver> observers = new ArrayList<>();
private BigDecimal price;
private String id;
public void setPrice(BigDecimal newPrice) {
if (!newPrice.equals(price)) {
PriceChangeEvent event = new PriceChangeEvent(id, price, newPrice, Instant.now());
price = newPrice;
notifyObservers(event);
}
}
// 实现其他方法...
}
// 库存系统观察者
public class InventoryObserver implements PriceObserver {
@Override
public void onPriceChanged(PriceChangeEvent event) {
// 更新库存缓存
System.out.println("库存系统: 更新产品 " + event.getProductId() + " 的价格缓存");
}
}
// 用户通知观察者
public class UserNotificationObserver implements PriceObserver {
@Override
public void onPriceChanged(PriceChangeEvent event) {
// 查询关注该商品的用户并发送通知
System.out.println("用户通知: 发送价格变化提醒给关注产品 " + event.getProductId() + " 的用户");
}
}
3.3 系统集成
java复制public class ECommerceSystem {
public static void main(String[] args) {
ProductPrice product = new ProductPrice("P123", new BigDecimal("99.99"));
// 注册观察者
product.addObserver(new InventoryObserver());
product.addObserver(new PromotionObserver());
product.addObserver(new UserNotificationObserver());
product.addObserver(new AnalyticsObserver());
// 价格变化触发通知
product.setPrice(new BigDecimal("89.99"));
// 输出:
// 库存系统: 更新产品 P123 的价格缓存
// 促销系统: 检查产品 P123 的价格变化是否符合促销条件
// 用户通知: 发送价格变化提醒给关注产品 P123 的用户
// 数据分析: 记录产品 P123 的价格变化历史
}
}
性能优化技巧:在实际项目中,我们会对高频价格变化的商品采用批量通知策略,比如每秒最多通知一次,而不是每次变化都立即通知。这显著降低了系统负载。
4. 常见问题与解决方案
4.1 观察者执行顺序问题
问题描述:多个观察者的通知顺序可能影响系统行为,但标准观察者模式不保证顺序。
解决方案:
- 在主题中维护优先级队列而非简单列表
- 使用Ordered接口标记观察者优先级
- 对于有严格顺序要求的场景,考虑责任链模式
java复制public interface OrderedObserver extends Observer {
int getOrder();
}
public class OrderedSubject implements Subject {
private PriorityQueue<OrderedObserver> queue = new PriorityQueue<>(
Comparator.comparingInt(OrderedObserver::getOrder));
public void registerObserver(OrderedObserver o) {
queue.add(o);
}
public void notifyObservers() {
while (!queue.isEmpty()) {
OrderedObserver observer = queue.poll();
observer.update(data);
}
}
}
4.2 观察者处理失败问题
问题描述:某个观察者处理通知时抛出异常,可能导致后续观察者不被通知。
解决方案:
- 为每个观察者添加try-catch块
- 使用装饰器模式包装观察者处理逻辑
- 记录失败观察者以便重试或排查
java复制public class SafeObserver implements Observer {
private Observer delegate;
public SafeObserver(Observer delegate) {
this.delegate = delegate;
}
@Override
public void update(Data data) {
try {
delegate.update(data);
} catch (Exception e) {
System.err.println("观察者处理失败: " + e.getMessage());
// 可添加重试逻辑或错误报告
}
}
}
// 使用方式
subject.registerObserver(new SafeObserver(new MyObserver()));
4.3 跨线程安全问题
问题描述:主题和观察者可能运行在不同线程中,导致并发问题。
解决方案:
- 使用线程安全的集合存储观察者
- 对关键操作添加同步控制
- 考虑使用并发工具类如CopyOnWriteArrayList
java复制public class ThreadSafeSubject implements Subject {
private final List<Observer> observers = new CopyOnWriteArrayList<>();
public void registerObserver(Observer o) {
observers.add(o);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(data);
}
}
}
5. 模式变体与相关模式
5.1 发布-订阅模式
虽然常被混用,发布-订阅模式与观察者模式有细微差别:
| 特性 | 观察者模式 | 发布-订阅模式 |
|---|---|---|
| 耦合度 | 松耦合 | 完全解耦 |
| 通信方式 | 直接调用 | 通过消息代理 |
| 实时性 | 通常同步 | 可同步可异步 |
| 典型实现 | Java Observable | Kafka, RabbitMQ |
5.2 与中介者模式的关系
中介者模式常使用观察者模式实现,但目的不同:
- 观察者:对象间动态的一对多关系
- 中介者:集中管理对象间的复杂交互
5.3 响应式编程中的观察者
现代响应式框架如RxJava、Reactor本质上是观察者模式的增强实现:
java复制// RxJava示例
Observable<Product> productObservable = Observable.create(emitter -> {
// 模拟价格变化
emitter.onNext(new Product("P1", 100));
Thread.sleep(1000);
emitter.onNext(new Product("P1", 90));
});
productObservable.subscribe(product -> {
System.out.println("价格更新: " + product.getPrice());
});
这种实现提供了更强大的功能:
- 丰富的操作符(map、filter等)
- 背压支持
- 线程调度控制
- 错误处理机制
6. 最佳实践与性能考量
6.1 何时使用观察者模式
适合场景:
- 一个对象状态变化需要通知其他对象,且不知道具体有多少对象需要通知
- 一个对象需要通知其他对象,但不想紧密耦合
- 需要跨系统或跨层通知,如GUI事件、领域事件
不适合场景:
- 通知链过长或观察者处理过于复杂,考虑使用消息队列
- 性能敏感场景,直接调用可能更高效
- 简单的一对一通信,直接调用更直接
6.2 性能优化技巧
- 懒通知:积累多个变化后一次性通知
java复制public class LazyProductPrice extends ProductPrice {
private boolean changed = false;
public void setPrice(BigDecimal newPrice) {
super.setPrice(newPrice);
changed = true;
}
public void notifyIfChanged() {
if (changed) {
notifyObservers();
changed = false;
}
}
}
- 差分通知:只发送变化的部分而非完整状态
java复制public interface DeltaObserver {
void onPriceChanged(String productId, BigDecimal delta);
}
- 过滤通知:允许观察者指定感兴趣的事件类型
java复制public interface FilteredObserver {
void update(Event event);
boolean isInterestedIn(EventType type);
}
public class FilteredSubject {
public void notifyObservers(Event event) {
observers.stream()
.filter(o -> o.isInterestedIn(event.getType()))
.forEach(o -> o.update(event));
}
}
6.3 测试观察者模式
测试观察者交互的几个策略:
- 使用Mock观察者验证通知是否正确发送
- 测试观察者注册和注销逻辑
- 验证通知顺序(如果重要)
- 测试并发场景下的线程安全
java复制@Test
public void testPriceChangeNotification() {
ProductPrice product = new ProductPrice("P1", BigDecimal.TEN);
MockObserver mock = new MockObserver();
product.addObserver(mock);
product.setPrice(new BigDecimal("9.99"));
assertTrue(mock.isNotified());
assertEquals(new BigDecimal("9.99"), mock.getLatestPrice());
}
7. 各语言实现差异
7.1 Java实现
Java标准库提供了Observable类和Observer接口,但不推荐使用:
- 不是接口,导致继承限制
- 方法同步可能造成性能问题
- 已标记为@Deprecated(since="9")
推荐自行实现或使用java.beans.PropertyChangeSupport:
java复制public class JavaBeanProduct {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private BigDecimal price;
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void setPrice(BigDecimal newPrice) {
BigDecimal old = this.price;
this.price = newPrice;
pcs.firePropertyChange("price", old, newPrice);
}
}
7.2 C++实现
C++实现需要注意内存管理,常用智能指针:
cpp复制class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
class Subject {
std::vector<std::weak_ptr<Observer>> observers;
public:
void addObserver(std::shared_ptr<Observer> observer) {
observers.emplace_back(observer);
}
void notifyObservers(const std::string& message) {
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto observer = it->lock()) {
observer->update(message);
++it;
} else {
it = observers.erase(it);
}
}
}
};
7.3 JavaScript实现
JavaScript天生适合观察者模式,常用EventEmitter:
javascript复制const EventEmitter = require('events');
class Product extends EventEmitter {
constructor(price) {
super();
this.price = price;
}
setPrice(newPrice) {
if (this.price !== newPrice) {
const oldPrice = this.price;
this.price = newPrice;
this.emit('priceChange', { oldPrice, newPrice });
}
}
}
const product = new Product(100);
product.on('priceChange', ({ oldPrice, newPrice }) => {
console.log(`价格从 ${oldPrice} 变为 ${newPrice}`);
});
7.4 Python实现
Python有多种实现方式,最简单的是使用属性观察:
python复制class Product:
def __init__(self, price):
self._price = price
self._observers = []
@property
def price(self):
return self._price
@price.setter
def price(self, value):
if self._price != value:
old = self._price
self._price = value
for callback in self._observers:
callback(old, value)
def add_observer(self, callback):
self._observers.append(callback)
def price_changed(old, new):
print(f"Price updated from {old} to {new}")
product = Product(100)
product.add_observer(price_changed)
product.price = 90
8. 实际项目经验分享
8.1 电商平台中的实战应用
在最近的一个电商项目中,我们使用观察者模式处理订单状态变化:
- 订单创建:通知库存系统、支付系统、促销系统
- 订单支付:通知物流系统、客服系统
- 订单取消:通知库存系统恢复库存、财务系统退款
java复制public class OrderService {
private final OrderObservable observable = new OrderObservable();
public void createOrder(Order order) {
// 创建订单逻辑...
observable.notifyObservers(new OrderEvent(OrderEventType.CREATED, order));
}
public void addObserver(OrderObserver observer) {
observable.addObserver(observer);
}
}
// 库存观察者
public class InventoryObserver implements OrderObserver {
@Override
public void onOrderEvent(OrderEvent event) {
if (event.getType() == OrderEventType.CREATED) {
decreaseStock(event.getOrder().getItems());
} else if (event.getType() == OrderEventType.CANCELLED) {
restoreStock(event.getOrder().getItems());
}
}
}
踩坑记录:最初我们同步通知所有观察者,导致下单接口响应变慢。后来改为异步通知,关键路径同步处理,非关键路径异步处理,系统性能显著提升。
8.2 微服务架构中的跨服务观察
在微服务架构下,我们使用Spring事件机制实现跨服务观察:
java复制// 定义领域事件
public class ProductPriceChangedEvent {
private String productId;
private BigDecimal oldPrice;
private BigDecimal newPrice;
// getters/setters...
}
// 发布事件
@Service
public class ProductService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void updatePrice(String productId, BigDecimal newPrice) {
// 更新价格逻辑...
eventPublisher.publishEvent(new ProductPriceChangedEvent(productId, oldPrice, newPrice));
}
}
// 订阅事件
@Service
public class PromotionService {
@EventListener
public void handlePriceChange(ProductPriceChangedEvent event) {
// 检查是否触发促销规则
}
}
这种实现方式的优势:
- 完全解耦发布者和订阅者
- 支持事务绑定(@TransactionalEventListener)
- 易于扩展新的订阅者
- 支持异步处理(@Async)
8.3 前端开发中的观察者模式
在前端框架中,观察者模式无处不在:
- React状态管理:Redux store作为主题,组件作为观察者
- Vue响应式系统:数据属性作为主题,计算属性和watcher作为观察者
- DOM事件:事件目标作为主题,事件监听器作为观察者
javascript复制// Vue响应式示例
const vm = new Vue({
data: {
price: 100
},
watch: {
price(newVal, oldVal) {
console.log(`价格从 ${oldVal} 变为 ${newVal}`);
}
}
});
// 修改价格会触发观察者
vm.price = 90;
前端开发中的经验:
- 避免过度订阅导致性能问题
- 注意内存泄漏,及时移除事件监听器
- 对于复杂状态变更,考虑使用状态机模式配合观察者
9. 设计模式组合应用
9.1 观察者+工厂模式
通过工厂创建标准化的观察者,简化观察者管理:
java复制public interface ObserverFactory {
PriceObserver createInventoryObserver();
PriceObserver createPromotionObserver();
PriceObserver createUserNotificationObserver();
}
public class DefaultObserverFactory implements ObserverFactory {
@Override
public PriceObserver createInventoryObserver() {
return new InventoryObserver();
}
// 其他工厂方法...
}
// 使用方式
ObserverFactory factory = new DefaultObserverFactory();
product.addObserver(factory.createInventoryObserver());
product.addObserver(factory.createPromotionObserver());
9.2 观察者+装饰器模式
通过装饰器增强观察者功能:
java复制public class LoggingObserverDecorator implements PriceObserver {
private final PriceObserver delegate;
private final Logger logger;
public LoggingObserverDecorator(PriceObserver delegate, Logger logger) {
this.delegate = delegate;
this.logger = logger;
}
@Override
public void onPriceChanged(PriceChangeEvent event) {
logger.info("价格变化事件: " + event);
delegate.onPriceChanged(event);
}
}
// 使用方式
product.addObserver(new LoggingObserverDecorator(new InventoryObserver(), logger));
9.3 观察者+策略模式
根据不同场景动态切换观察者行为:
java复制public class ConfigurableObserver implements PriceObserver {
private PriceChangeStrategy strategy;
public ConfigurableObserver(PriceChangeStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(PriceChangeStrategy strategy) {
this.strategy = strategy;
}
@Override
public void onPriceChanged(PriceChangeEvent event) {
strategy.handlePriceChange(event);
}
}
public interface PriceChangeStrategy {
void handlePriceChange(PriceChangeEvent event);
}
// 使用方式
ConfigurableObserver observer = new ConfigurableObserver(new DefaultPriceStrategy());
product.addObserver(observer);
// 运行时切换策略
observer.setStrategy(new SpecialPromotionStrategy());
10. 反模式与常见错误
10.1 过度使用观察者
问题表现:
- 系统中到处都是事件和监听器
- 事件流难以追踪和理解
- 调试困难,因果关系不明显
解决方案:
- 限制观察者使用范围
- 为事件流添加文档和日志
- 考虑使用显式调用替代部分观察者
10.2 循环通知
问题表现:
- 观察者A通知B,B又通知A
- 系统陷入无限循环或栈溢出
解决方案:
- 检测和防止循环通知
- 使用标记防止重入
- 设计更清晰的事件流向
java复制public class SafeSubject implements Subject {
private boolean notifying = false;
public void notifyObservers() {
if (notifying) return;
try {
notifying = true;
// 实际通知逻辑...
} finally {
notifying = false;
}
}
}
10.3 忽略线程安全问题
问题表现:
- 并发环境下观察者列表损坏
- 通知过程中列表被修改
- 观察者状态不一致
解决方案:
- 使用线程安全的集合
- 对关键操作加锁
- 考虑不可变事件对象
java复制public class ConcurrentSubject implements Subject {
private final CopyOnWriteArrayList<Observer> observers = new CopyOnWriteArrayList<>();
public void registerObserver(Observer o) {
observers.addIfAbsent(o);
}
public void notifyObservers() {
for (Observer o : observers) {
o.update();
}
}
}
10.4 忽略性能影响
问题表现:
- 通知链过长导致性能下降
- 观察者处理耗时影响主流程
- 高频事件导致系统过载
解决方案:
- 异步处理非关键通知
- 批量处理高频事件
- 监控和优化热点路径
java复制public class BatchSubject implements Subject {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<Event> batch = new ArrayList<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public BatchSubject() {
scheduler.scheduleAtFixedRate(this::flushBatch, 1, 1, TimeUnit.SECONDS);
}
public void notifyObservers(Event event) {
synchronized (batch) {
batch.add(event);
}
}
private void flushBatch() {
List<Event> currentBatch;
synchronized (batch) {
currentBatch = new ArrayList<>(batch);
batch.clear();
}
if (!currentBatch.isEmpty()) {
executor.submit(() -> {
for (Observer o : observers) {
o.handleBatch(currentBatch);
}
});
}
}
}
11. 现代替代方案
11.1 响应式流(Reactive Streams)
响应式编程库如RxJava、Project Reactor提供了更强大的观察者模式实现:
java复制// RxJava示例
Observable<Product> productObservable = Observable.create(emitter -> {
// 模拟价格变化
emitter.onNext(new Product("P1", 100));
Thread.sleep(1000);
emitter.onNext(new Product("P1", 90));
});
Disposable subscription = productObservable
.filter(p -> p.getPrice() < 95)
.subscribe(
product -> System.out.println("促销价格: " + product.getPrice()),
error -> System.err.println("错误: " + error),
() -> System.out.println("完成")
);
// 取消订阅
subscription.dispose();
优势:
- 丰富的操作符
- 背压支持
- 线程调度控制
- 错误处理机制
11.2 消息队列(Message Queue)
对于分布式系统,消息队列是更强大的观察者模式实现:
java复制// RabbitMQ示例
public class PriceChangeProducer {
private final Channel channel;
public PriceChangeProducer(Connection connection) throws IOException {
channel = connection.createChannel();
channel.exchangeDeclare("price_changes", "fanout");
}
public void publishChange(PriceChangeEvent event) throws IOException {
channel.basicPublish("price_changes", "", null,
new ObjectMapper().writeValueAsBytes(event));
}
}
public class InventoryConsumer {
public void start(Connection connection) throws IOException {
Channel channel = connection.createChannel();
channel.exchangeDeclare("price_changes", "fanout");
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, "price_changes", "");
channel.basicConsume(queue, true, (consumerTag, delivery) -> {
PriceChangeEvent event = new ObjectMapper()
.readValue(delivery.getBody(), PriceChangeEvent.class);
updateInventoryCache(event);
}, consumerTag -> {});
}
}
11.3 数据绑定框架
现代UI框架如Android的Data Binding、JavaFX的Properties提供了内置的观察机制:
java复制// JavaFX示例
SimpleIntegerProperty price = new SimpleIntegerProperty(100);
price.addListener((observable, oldValue, newValue) -> {
System.out.println("价格变化: " + oldValue + " -> " + newValue);
});
price.set(90); // 自动触发监听器
12. 总结与个人建议
经过多年在各种项目中使用观察者模式,我总结了以下几点经验:
-
明确边界:清晰定义什么情况下使用观察者模式,避免滥用。最适合处理"不知道也不关心有多少对象需要被通知"的场景。
-
控制复杂度:当观察者逻辑变得复杂时,考虑引入中介者或命令模式来简化。
-
性能考量:在高频事件场景下,务必考虑批处理和异步通知。
-
测试策略:观察者交互容易引入隐蔽的bug,需要特别注意单元测试和集成测试覆盖。
-
文档记录:为事件流和维护的观察者关系添加文档,方便后续维护。
-
现代替代:在新项目中,考虑使用响应式编程库或消息队列等更现代的解决方案。
最后分享一个实用技巧:在实现观察者模式时,我通常会添加一个调试观察者,用于记录所有事件流,这在排查问题时非常有用:
java复制public class DebugObserver implements Observer {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void update(Event event) {
logger.debug("事件通知: {}", event);
// 可以添加堆栈跟踪记录
if (logger.isTraceEnabled()) {
logger.trace("事件来源堆栈:", new Exception("调试堆栈"));
}
}
}
// 注册方式
subject.addObserver(new DebugObserver());
