每日一学:设计模式之观察者模式
观察者模式(Observer Pattern)属于行为型设计模式,核心定义:构建对象间一对多的依赖关系,当被观察者(发布者 / 主题)状态发生变化时,所有订阅它的观察者(订阅者)会自动收到通知并执行更新逻辑。
别名也叫:发布 - 订阅模式、监听者模式、Event Listener,是日常开发最常用、最易理解的设计模式之一。
常见的观察者模式应用
消息队列、redis发布订阅、spring事件通知
一、观察者模式能解决什么问题
举例
杂志订阅、公众号推送、商城降价提醒、天气预报推送:你订阅公众号后,博主发布新文章(状态变更),所有粉丝自动接收推送,无需每个人主动刷新查看。
如果不使用观察者模式:
- 硬编码耦合:一个业务变更,需要手动调用多个模块的方法;
- 扩展性极差:新增业务分支,必须修改原有核心代码;
- 维护混乱:对象之间强依赖,一处改动引发连锁 bug。
观察者模式完美解决:解耦核心业务与后续联动逻辑,核心类只负责自身业务,无需关心有多少下游依赖方。
二、核心角色拆解
- 抽象被观察者(Subject)维护观察者集合,提供订阅、取消订阅、批量通知的抽象方法。
- 具体被观察者(Concrete Subject)持有自身状态,状态变更后,触发通知方法,推送消息给所有观察者。
- 抽象观察者(Observer)统一更新接口,定义
update()方法,接收被观察者的通知。 - 具体观察者(Concrete Observer)实现更新接口,自定义收到通知后的业务逻辑。
- 客户端组装被观察者与观察者,完成订阅绑定。
三、手写 Java 完整实现
1. 抽象观察者接口
public interface Observer { // 接收通知、执行更新 void update(String message); }2. 抽象被观察者
public abstract class Subject { // 维护观察者列表 protected List<Observer> observerList = new ArrayList<>(); // 订阅 public abstract void addObserver(Observer observer); // 取消订阅 public abstract void removeObserver(Observer observer); // 通知所有观察者 public abstract void notifyObservers(String message); }3. 具体被观察者(消息发布方)
public class MessageSubject extends Subject { @Override public void addObserver(Observer observer) { observerList.add(observer); } @Override public void removeObserver(Observer observer) { observerList.remove(observer); } @Override public void notifyObservers(String message) { // 遍历所有观察者,推送消息 for (Observer observer : observerList) { observer.update(message); } } }4. 具体观察者(多端接收方)
// 短信通知观察者 public class SmsObserver implements Observer { @Override public void update(String message) { System.out.println("短信接收通知:" + message); } } // 邮件通知观察者 public class EmailObserver implements Observer { @Override public void update(String message) { System.out.println("邮件接收通知:" + message); } } // 站内信观察者 public class WebObserver implements Observer { @Override public void update(String message) { System.out.println("站内信接收通知:" + message); } }5. 测试
public class ObserverDemo { public static void main(String[] args) { // 1. 创建被观察者 MessageSubject subject = new MessageSubject(); // 2. 创建多个观察者 Observer sms = new SmsObserver(); Observer email = new EmailObserver(); Observer web = new WebObserver(); // 3. 绑定订阅关系 subject.addObserver(sms); subject.addObserver(email); subject.addObserver(web); // 4. 被观察者状态变更,自动推送通知 System.out.println("===商城活动更新==="); subject.notifyObservers("双十一活动正式开启,全场五折!"); // 5. 取消单个订阅 subject.removeObserver(web); System.out.println("\n===价格变更通知==="); subject.notifyObservers("爆款商品限时降价200元"); } }运行结果
===商城活动更新=== 短信接收通知:双十一活动正式开启,全场五折! 邮件接收通知:双十一活动正式开启,全场五折! 站内信接收通知:双十一活动正式开启,全场五折! ===价格变更通知=== 短信接收通知:爆款商品限时降价200元 邮件接收通知:爆款商品限时降价200元四、JDK 原生观察者支持(拓展)
Java 内置提供了观察者模式原生实现,无需手动编写集合管理:
- 被观察者:继承
Observable - 观察者:实现
Observer接口
注意:JDK 原生实现已标记过时,不推荐生产使用,仅作学习参考。
五、推模式 vs 拉模式
- 推模式(示例实现)被观察者主动推送完整数据给观察者,观察者直接使用,耦合低、使用简单。
- 拉模式被观察者只推送变更信号,观察者主动拉取被观察者最新数据,适合大数据、复杂场景。
六、优缺点分析
优点
- 符合开闭原则新增观察者无需修改被观察者代码,直接新增实现类即可。
- 松耦合设计被观察者与观察者仅通过接口依赖,互不干涉内部逻辑。
- 动态订阅运行时可以灵活新增、取消订阅,动态调整依赖关系。
- 事件联动解耦一套状态变更,自动触发多模块联动,代码简洁优雅。
缺点
- 通知顺序不可控默认遍历列表顺序推送,无法自定义优先级。
- 性能损耗观察者数量过多时,循环通知会产生耗时,同步阻塞影响性能。
- 循环依赖风险若观察者内部反向修改被观察者状态,容易引发死循环。
- 内存泄漏观察者未主动取消订阅,会被被观察者强引用,导致无法回收。
