设计模式-观察者模式详解
观察者模式详解
目录
- 观察者模式简介
- 核心流程
- 重难点分析
- Spring中的源码分析
- 具体使用场景
- 面试高频点
观察者模式简介
定义
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。
核心思想
- 解耦:观察者和被观察者之间松耦合
- 通知机制:当状态改变时自动通知所有观察者
- 动态关系:观察者可以动态地添加和删除
模式结构
- Subject(主题/被观察者):定义添加、删除、通知观察者的接口
- ConcreteSubject(具体主题):实现主题接口,维护观察者列表
- Observer(观察者):定义更新接口
- ConcreteObserver(具体观察者):实现观察者接口,定义具体的更新逻辑
核心流程
观察者模式流程图
基本实现流程
1. 定义观察者接口
public interface Observer {void update(String message);
}
2. 定义主题接口
public interface Subject {void addObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
3. 实现具体主题
public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();private String state;@Overridepublic void addObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(state);}}public void setState(String state) {this.state = state;notifyObservers(); // 状态改变时通知观察者}
}
4. 实现具体观察者
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " 收到消息: " + message);}
}
5. 客户端使用
public class Client {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("观察者1");Observer observer2 = new ConcreteObserver("观察者2");subject.addObserver(observer1);subject.addObserver(observer2);subject.setState("状态已改变");}
}
重难点分析
重难点1:内存泄漏问题
问题描述
观察者模式容易导致内存泄漏,特别是当观察者没有正确注销时。
解决方案
public class SubjectManager {private static final Map<String, Subject> subjects = new ConcurrentHashMap<>();public static void registerObserver(String subjectId, Observer observer) {subjects.computeIfAbsent(subjectId, k -> new ConcreteSubject()).addObserver(observer);}public static void unregisterObserver(String subjectId, Observer observer) {Subject subject = subjects.get(subjectId);if (subject != null) {subject.removeObserver(observer);// 如果没有观察者了,可以考虑清理subjectif (subject.getObserverCount() == 0) {subjects.remove(subjectId);}}}
}
重难点2:线程安全问题
问题描述
在多线程环境下,观察者列表的修改和遍历可能产生并发问题。
解决方案
public class ThreadSafeSubject implements Subject {private final List<Observer> observers = Collections.synchronizedList(new ArrayList<>());private final Object lock = new Object();@Overridepublic void addObserver(Observer observer) {synchronized (lock) {if (!observers.contains(observer)) {observers.add(observer);}}}@Overridepublic void removeObserver(Observer observer) {synchronized (lock) {observers.remove(observer);}}@Overridepublic void notifyObservers() {List<Observer> observersCopy;synchronized (lock) {observersCopy = new ArrayList<>(observers);}for (Observer observer : observersCopy) {try {observer.update(getState());} catch (Exception e) {// 处理观察者异常,避免影响其他观察者System.err.println("观察者更新失败: " + e.getMessage());}}}
}
重难点3:观察者执行顺序
问题描述
多个观察者的执行顺序可能影响业务逻辑。
解决方案
public class PriorityObserver implements Observer, Comparable<PriorityObserver> {private final Observer observer;private final int priority;public PriorityObserver(Observer observer, int priority) {this.observer = observer;this.priority = priority;}@Overridepublic void update(String message) {observer.update(message);}@Overridepublic int compareTo(PriorityObserver other) {return Integer.compare(this.priority, other.priority);}
}public class OrderedSubject implements Subject {private final List<PriorityObserver> observers = new ArrayList<>();@Overridepublic void notifyObservers() {// 按优先级排序后通知observers.stream().sorted().forEach(observer -> observer.update(getState()));}
}
重难点4:观察者异常处理
问题描述
某个观察者抛出异常可能影响其他观察者的执行。
解决方案
public class SafeObserverWrapper implements Observer {private final Observer delegate;private final String observerName;public SafeObserverWrapper(Observer delegate, String observerName) {this.delegate = delegate;this.observerName = observerName;}@Overridepublic void update(String message) {try {delegate.update(message);} catch (Exception e) {// 记录异常但不影响其他观察者System.err.println("观察者 " + observerName + " 执行失败: " + e.getMessage());// 可以选择移除异常的观察者// removeObserver(this);}}
}
Spring中的源码分析
ApplicationEventPublisher接口
@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);
}
AbstractApplicationContext中的实现
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {private ApplicationEventMulticaster applicationEventMulticaster;@Overridepublic void publishEvent(ApplicationEvent event) {publishEvent(event, null);}@Overridepublic void publishEvent(Object event) {publishEvent(event, null);}protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");// 包装事件ApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;} else {applicationEvent = new PayloadApplicationEvent<>(this, event);}// 发布事件if (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);} else {getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// 发布到父上下文if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);} else {this.parent.publishEvent(event);}}}
}
ApplicationEventMulticaster接口
public interface ApplicationEventMulticaster {void addApplicationListener(ApplicationListener<?> listener);void addApplicationListenerBean(String listenerBeanName);void removeApplicationListener(ApplicationListener<?> listener);void removeApplicationListenerBean(String listenerBeanName);void removeAllListeners();void multicastEvent(ApplicationEvent event);void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
SimpleApplicationEventMulticaster实现
public class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster {@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));} else {invokeListener(listener, event);}}}@SuppressWarnings({"rawtypes", "unchecked"})private void invokeListener(ApplicationListener listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {listener.onApplicationEvent(event);} catch (Throwable err) {errorHandler.handleError(err);}} else {listener.onApplicationEvent(event);}}
}
自定义事件示例
// 自定义事件
public class UserRegisteredEvent extends ApplicationEvent {private final String username;private final String email;public UserRegisteredEvent(Object source, String username, String email) {super(source);this.username = username;this.email = email;}public String getUsername() { return username; }public String getEmail() { return email; }
}// 事件监听器
@Component
public class UserEventListener {@EventListenerpublic void handleUserRegistered(UserRegisteredEvent event) {System.out.println("用户注册事件: " + event.getUsername());// 发送欢迎邮件sendWelcomeEmail(event.getEmail());}@EventListener@Asyncpublic void handleUserRegisteredAsync(UserRegisteredEvent event) {// 异步处理System.out.println("异步处理用户注册: " + event.getUsername());}private void sendWelcomeEmail(String email) {// 发送邮件逻辑}
}// 事件发布者
@Service
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void registerUser(String username, String email) {// 注册用户逻辑System.out.println("用户注册成功: " + username);// 发布事件eventPublisher.publishEvent(new UserRegisteredEvent(this, username, email));}
}
具体使用场景
1. 用户注册系统
// 用户注册后需要执行多个操作
@Component
public class UserRegistrationService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void registerUser(User user) {// 保存用户saveUser(user);// 发布注册事件eventPublisher.publishEvent(new UserRegisteredEvent(user));}
}// 发送欢迎邮件
@Component
public class WelcomeEmailListener {@EventListenerpublic void handleUserRegistered(UserRegisteredEvent event) {sendWelcomeEmail(event.getUser().getEmail());}
}// 初始化用户权限
@Component
public class UserPermissionListener {@EventListenerpublic void handleUserRegistered(UserRegisteredEvent event) {initializeUserPermissions(event.getUser());}
}
2. 订单状态变更
// 订单状态变更事件
public class OrderStatusChangedEvent extends ApplicationEvent {private final Order order;private final OrderStatus oldStatus;private final OrderStatus newStatus;// 构造函数和getter方法
}// 订单服务
@Service
public class OrderService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void updateOrderStatus(Long orderId, OrderStatus newStatus) {Order order = getOrderById(orderId);OrderStatus oldStatus = order.getStatus();order.setStatus(newStatus);saveOrder(order);// 发布状态变更事件eventPublisher.publishEvent(new OrderStatusChangedEvent(order, oldStatus, newStatus));}
}// 库存更新监听器
@Component
public class InventoryUpdateListener {@EventListenerpublic void handleOrderStatusChanged(OrderStatusChangedEvent event) {if (event.getNewStatus() == OrderStatus.CANCELLED) {// 释放库存releaseInventory(event.getOrder());}}
}// 物流通知监听器
@Component
public class LogisticsNotificationListener {@EventListenerpublic void handleOrderStatusChanged(OrderStatusChangedEvent event) {if (event.getNewStatus() == OrderStatus.SHIPPED) {// 通知物流notifyLogistics(event.getOrder());}}
}
3. 缓存更新通知
// 缓存更新事件
public class CacheUpdateEvent extends ApplicationEvent {private final String cacheKey;private final Object oldValue;private final Object newValue;// 构造函数和getter方法
}// 缓存服务
@Service
public class CacheService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void updateCache(String key, Object value) {Object oldValue = getFromCache(key);putToCache(key, value);// 发布缓存更新事件eventPublisher.publishEvent(new CacheUpdateEvent(key, oldValue, value));}
}// 分布式缓存同步监听器
@Component
public class DistributedCacheSyncListener {@EventListenerpublic void handleCacheUpdate(CacheUpdateEvent event) {// 同步到其他节点syncToOtherNodes(event.getCacheKey(), event.getNewValue());}
}
4. 系统监控和日志
// 系统事件
public class SystemEvent extends ApplicationEvent {private final String eventType;private final String message;private final Map<String, Object> metadata;// 构造函数和getter方法
}// 系统监控服务
@Service
public class SystemMonitorService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void recordSystemEvent(String eventType, String message, Map<String, Object> metadata) {eventPublisher.publishEvent(new SystemEvent(eventType, message, metadata));}
}// 日志记录监听器
@Component
public class LoggingListener {@EventListenerpublic void handleSystemEvent(SystemEvent event) {// 记录到日志文件logger.info("系统事件: {} - {}", event.getEventType(), event.getMessage());}
}// 监控指标收集监听器
@Component
public class MetricsCollectorListener {@EventListenerpublic void handleSystemEvent(SystemEvent event) {// 收集监控指标collectMetrics(event.getEventType(), event.getMetadata());}
}
面试高频点
面试知识点思维导图
1. 观察者模式的基本概念
问题:什么是观察者模式?
答案要点:
- 定义一对多的依赖关系
- 当主题状态改变时,自动通知所有观察者
- 实现松耦合的设计
- 属于行为型设计模式
问题:观察者模式有哪些角色?
答案要点:
- Subject(主题):被观察的对象,维护观察者列表
- Observer(观察者):观察主题的对象,定义更新接口
- ConcreteSubject(具体主题):实现主题接口
- ConcreteObserver(具体观察者):实现观察者接口
2. 实现方式相关
问题:如何实现观察者模式?
答案要点:
// 1. 定义观察者接口
public interface Observer {void update(String message);
}// 2. 定义主题接口
public interface Subject {void addObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 3. 实现具体主题
public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();private String state;public void setState(String state) {this.state = state;notifyObservers();}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(state);}}
}
3. 重难点问题
问题:观察者模式有什么缺点?
答案要点:
- 内存泄漏:观察者没有正确注销
- 循环依赖:观察者和主题相互引用
- 性能问题:大量观察者时通知效率低
- 线程安全:多线程环境下的并发问题
- 执行顺序:观察者执行顺序不确定
问题:如何解决观察者模式的内存泄漏问题?
答案要点:
// 1. 使用WeakReference
public class WeakObserver implements Observer {private WeakReference<Observer> delegate;public WeakObserver(Observer delegate) {this.delegate = new WeakReference<>(delegate);}@Overridepublic void update(String message) {Observer observer = delegate.get();if (observer != null) {observer.update(message);}}
}// 2. 及时注销观察者
public void cleanup() {subject.removeObserver(this);
}
4. Spring中的应用
问题:Spring中如何使用观察者模式?
答案要点:
// 1. 定义事件
public class UserRegisteredEvent extends ApplicationEvent {private final String username;public UserRegisteredEvent(Object source, String username) {super(source);this.username = username;}
}// 2. 发布事件
@Service
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void registerUser(String username) {// 注册逻辑eventPublisher.publishEvent(new UserRegisteredEvent(this, username));}
}// 3. 监听事件
@Component
public class UserEventListener {@EventListenerpublic void handleUserRegistered(UserRegisteredEvent event) {System.out.println("用户注册: " + event.getUsername());}
}
问题:Spring事件机制的特点?
答案要点:
- 同步发布:默认同步执行
- 异步支持:使用@Async注解
- 事件传播:支持父子容器事件传播
- 类型安全:基于泛型的事件类型
- 异常处理:支持异常处理器
5. 设计原则相关
问题:观察者模式体现了哪些设计原则?
答案要点:
- 开闭原则:可以添加新的观察者而不修改主题
- 依赖倒置:依赖抽象而不是具体实现
- 单一职责:主题和观察者职责分离
- 里氏替换:观察者可以替换
6. 实际应用场景
问题:观察者模式适用于哪些场景?
答案要点:
- 用户注册:注册后发送邮件、初始化权限等
- 订单状态:状态变更后更新库存、通知物流等
- 缓存更新:数据变更后同步缓存
- 系统监控:记录日志、收集指标等
- UI更新:模型变更后更新视图
7. 与其他模式的对比
问题:观察者模式与发布-订阅模式的区别?
答案要点:
- 耦合度:观察者模式耦合度更高
- 通信方式:观察者模式直接调用,发布-订阅通过消息队列
- 扩展性:发布-订阅模式扩展性更好
- 性能:发布-订阅模式性能更好
问题:观察者模式与中介者模式的区别?
答案要点:
- 关系:观察者是一对多,中介者是多对多
- 职责:观察者负责通知,中介者负责协调
- 复杂度:中介者模式更复杂
- 使用场景:观察者用于状态通知,中介者用于对象协调
总结
观察者模式是一种重要的行为型设计模式,它通过定义一对多的依赖关系,实现了对象间的松耦合通信。在Spring框架中,观察者模式被广泛应用于事件驱动编程,提供了强大的解耦和扩展能力。
核心优势
- 松耦合:观察者和主题之间松耦合
- 动态关系:可以动态添加和删除观察者
- 开闭原则:易于扩展新的观察者
- 通知机制:自动通知所有观察者
注意事项
- 内存管理:注意观察者的生命周期管理
- 线程安全:多线程环境下的并发控制
- 异常处理:避免单个观察者异常影响其他观察者
- 性能考虑:大量观察者时的性能优化
在实际开发中,观察者模式特别适用于需要解耦的业务场景,如用户注册、订单状态变更、缓存更新等。通过合理使用观察者模式,可以大大提高代码的可维护性和扩展性。