别再 if-else 疯狂嵌套了!观察者模式让你代码优雅到离谱
一、问题背景为什么写这篇最近在做一个小需求——订单状态变更后要同时触发库存扣减、短信通知、积分变更、日志记录……我第一反应是什么简单啊if(order.getStatus()PAID){inventoryService.deduct(order);smsService.sendNotice(order);pointsService.addPoints(order);logService.saveLog(order);}if(order.getStatus()PAID){inventoryService.deduct(order);smsService.sendNotice(order);pointsService.addPoints(order);logService.saveLog(order);couponService.issueCoupon(order);// 新加的statsService.record(order);// 新加的pushService.push(order);// 新加的// 未来还有 N 个服务等着加...}这就是典型的牵一发动全身——每加一个功能都得改订单核心逻辑改完还得重新测试整个流程。程序跑起来了但维护的人头发少了 后来我想起来了——这不就是观察者模式的经典场景吗谁该干活谁自己订阅就行了别都挤到订单里来二、观察者模式是什么用一句话说一个对象状态变了所有依赖它的对象自动收到通知并更新。生活中的例子微信群有人发消息所有群成员都能收到B站关注UP主发新视频粉丝收到推送红绿灯灯变了所有车和行人同时响应核心思想就四个字发布-订阅。三、UML 结构先看图再写代码┌─────────────┐ ┌─────────────┐ │ Subject │ │ Observer │ │ (被观察者) │─────│ (观察者接口) │ │─────────────│ │─────────────│ │attach() │ │update() │ │detach() │ └─────────────┘ │notify() │ △ └──────┬──────┘ │ △ │ │ ┌───────┴───────┐ │ │ │ ┌──────┴──────┐ ┌────┴────┐ ┌──────┴────┐ │ConcreteSubj │ │ObserverA│ │ObserverB │ │(具体被观察者) │ │(具体观察者)│ │(具体观察者) │ └─────────────┘ └─────────┘ └───────────┘两个角色Subject被观察者维护观察者列表状态变化时通知所有人Observer观察者定义更新接口收到通知后做自己该做的事关键原则被观察者不关心观察者具体是谁、具体干什么只负责喊一嗓子。这就是松耦合的精髓。四、手写实现从 0 到 1 撸一个观察者模式1. 定义观察者接口/** * 观察者接口 - 所有订阅者必须实现 */publicinterfaceObserver{/** * 收到通知后的处理逻辑 * param order 订单信息 */voidupdate(Orderorder);}2. 定义被观察者Subject/** * 被观察者 - 订单事件发布者 */publicclassOrderEventPublisher{/** 观察者列表谁订阅了谁就在这里面 */privatefinalListObserverobserversnewArrayList();/** * 订阅 */publicvoidattach(Observerobserver){observers.add(observer);}/** * 取消订阅 */publicvoiddetach(Observerobserver){observers.remove(observer);}/** * 通知所有观察者广播模式 */publicvoidnotifyObservers(Orderorder){for(Observerobserver:observers){observer.update(order);}}}3. 具体观察者——各干各的/** * 库存扣减观察者 */publicclassInventoryObserverimplementsObserver{Overridepublicvoidupdate(Orderorder){System.out.println(库存扣减商品 order.getProductId() 扣减 order.getQuantity());}}/** * 短信通知观察者 */publicclassSmsObserverimplementsObserver{Overridepublicvoidupdate(Orderorder){System.out.println(短信通知订单 order.getOrderId() 已支付请尽快发货);}}/** * 积分变更观察者 */publicclassPointsObserverimplementsObserver{Overridepublicvoidupdate(Orderorder){System.out.println(积分变更用户 order.getUserId() 增加 order.getAmount() 积分);}}4. 客户端调用publicclassOrderService{privatefinalOrderEventPublisherpublishernewOrderEventPublisher();publicOrderService(){// 订阅谁该干活自己注册publisher.attach(newInventoryObserver());publisher.attach(newSmsObserver());publisher.attach(newPointsObserver());// 以后要加功能new 一个 Observerattach 进来就行}/** * 订单支付成功 */publicvoidpayOrder(Orderorder){// 1. 核心业务更新订单状态order.setStatus(OrderStatus.PAID);System.out.println(订单 order.getOrderId() 支付成功);// 2. 发布事件一嗓子喊完各干各的publisher.notifyObservers(order);}}看明白了吗OrderService只负责核心逻辑——更新订单状态。至于库存、短信、积分它根本不管以后要加优惠券加两行代码publisher.attach(newCouponObserver());// 搞定订单核心逻辑一行都不用改这就是观察者模式的威力——开闭原则的完美体现。五、JDK 自带观察者别重复造轮子Java 其实早就内置了观察者模式的支持java.util.Observable被观察者java.util.Observer观察者接口用法很简单// 被观察者publicclassOrderObservableextendsObservable{publicvoidorderPaid(Orderorder){setChanged();// 标记状态已改变notifyObservers(order);// 通知观察者}}// 观察者publicclassInventoryObserverimplementsObserver{Overridepublicvoidupdate(Observableo,Objectarg){Orderorder(Order)arg;System.out.println(库存扣减order.getProductId());}}但是注意——Observable和Observer在Java 9 已被标记为 Deprecated官方理由Observable是类不是接口无法继承其他类setChanged()是 protected扩展不灵活notifyObservers()是同步的线程不安全整体设计老旧不符合现代 Java 风格官方推荐用java.beans.PropertyChangeListener或第三方框架替代。六、实战推荐Spring 事件机制才是 Javaer 的首选在企业级 Spring 项目中观察者模式的最佳实践是 Spring Event。1. 定义事件/** * 订单支付事件 */publicclassOrderPaidEventextendsApplicationEvent{privatefinalOrderorder;publicOrderPaidEvent(Objectsource,Orderorder){super(source);this.orderorder;}publicOrdergetOrder(){returnorder;}}2. 发布事件ServicepublicclassOrderService{AutowiredprivateApplicationEventPublishereventPublisher;publicvoidpayOrder(Orderorder){// 1. 核心业务order.setStatus(OrderStatus.PAID);// 2. 发布事件一行搞定eventPublisher.publishEvent(newOrderPaidEvent(this,order));}}3. 监听事件——各模块自己管自己ComponentpublicclassInventoryListener{EventListenerpublicvoidonOrderPaid(OrderPaidEventevent){Orderorderevent.getOrder();System.out.println(库存扣减order.getProductId());}}ComponentpublicclassSmsListener{EventListenerpublicvoidonOrderPaid(OrderPaidEventevent){Orderorderevent.getOrder();System.out.println(短信通知订单 order.getOrderId() 已支付);}}优势明显完全解耦发布者不知道谁在监听零侵入新功能加个EventListener就行Spring 原生不用自己维护观察者列表支持异步加个Async就能异步处理4. 异步监听性能优化利器ComponentpublicclassPointsListener{AsyncEventListenerpublicvoidonOrderPaid(OrderPaidEventevent){// 积分变更不急异步处理就行Orderorderevent.getOrder();System.out.println(异步积分变更order.getUserId());}}以为短信是同步阻塞的加个Async秒变异步订单接口响应速度直接翻倍。七、观察者模式 vs 其他模式别搞混了对比项观察者模式策略模式发布-订阅模式核心目的一对多通知算法替换消息解耦耦合度松耦合知道对方存在松耦合完全解耦通过 Broker通信方式直接调用直接调用通过消息中间件典型场景事件通知支付方式切换Kafka/RabbitMQJava 实现Observer/EventStrategy 接口MessageQueue最容易混淆的观察者模式 vs 发布-订阅模式观察者Subject 直接调用 Observer两者有直接依赖发布-订阅通过 EventBroker 中转发布者和订阅者互不知道简单说观察者是当面喊发布-订阅是发群里。八、避坑指南这坑我替你踩过了我踩过的坑坑 1观察者执行异常导致链路中断publicvoidnotifyObservers(Orderorder){for(Observerobserver:observers){observer.update(order);// 如果某个 Observer 抛异常后面的全挂}}修复加 try-catch一个挂了不影响其他人publicvoidnotifyObservers(Orderorder){for(Observerobserver:observers){try{observer.update(order);}catch(Exceptione){log.error(观察者执行异常: {},observer.getClass().getSimpleName(),e);}}}别急着 CtrlC / CtrlV先想清楚异常会不会传染。坑 2Spring EventListener 默认同步执行如果某个监听器很慢比如发邮件整个订单接口都会被阻塞。修复AsyncEventListenerpublicvoidonOrderPaid(OrderPaidEventevent){// 耗时操作放这里}别忘了在启动类加EnableAsync坑 3观察者注册顺序依赖如果库存扣减必须在积分变更之前执行——观察者模式本身不保证顺序修复Spring Event 支持Order注解ComponentOrder(1)publicclassInventoryListener{EventListenerpublicvoidonOrderPaid(OrderPaidEventevent){...}}ComponentOrder(2)publicclassPointsListener{EventListenerpublicvoidonOrderPaid(OrderPaidEventevent){...}}容易忽略的问题内存泄漏观察者没及时 detach对象无法被 GC 回收尤其是 Android 开发中常见死循环观察者 A 更新后触发了被观察者状态变化又被通知又更新……无限循环并发问题多线程下观察者列表的增删和遍历可能抛ConcurrentModificationException线上环境注意事项异步观察者一定要加异常处理否则错误会被吞掉日志都看不到生产环境建议使用Spring Event Async 线程池别用默认线程池关键业务通知考虑用消息队列RocketMQ/Kafka替代内存级观察者防止服务重启丢消息性能建议观察者数量控制在合理范围别搞 50 个监听者那是设计有问题耗时操作务必异步化高频事件注意节流和防抖安全建议敏感操作不要放观察者中无鉴权执行事件对象中不要传递敏感信息的明文如密码、密钥监听器中的异常日志脱敏处理别把用户隐私打到日志里九、总结项目说明模式类型行为型模式核心思想一对多依赖状态变化自动通知解决问题对象间紧耦合、if-else 嵌套地狱实现方式手写 Observer / JDK Observable已废弃/ Spring Event推荐方案Spring Event Async适用场景事件通知、消息广播、状态变更联动一句话总结观察者模式的本质就是——你别来找我我变了会通知你。这跟现实生活中的道理一样与其天天盯着领导脸色轮询不如等领导发通知观察者。技术不难难的是没人告诉你坑在哪。如果这篇帮到你点个赞顺便帮我抢救一下发际线 三连支持动力源泉如果这篇文章帮你省下了踩坑的时间欢迎点赞—— 让更多人看到这篇干货在看—— 你的认可是我持续输出的动力转发—— 分享给身边正在做AI Agent的朋友你的每一个小动作对我都很重要 ❤️ 关于作者你好我是空门技术栈一个常年和Bug战斗、持续填坑的Java开发者。专注分享✅ Java / Spring Boot / Spring AI Alibaba 企业级实战✅ RAG知识库、AI Agent、多智能体协作落地经验✅ Docker部署、微服务架构、线上问题排查✅ 偶尔聊聊「如何保住头发」这类程序员终极话题 不搞水文不贩卖焦虑只写能跑通、能落地、能帮你少加班的实战内容。关注我咱们一起少踩坑多写优雅代码。 更多干货推荐告别手动复制接口文档Apifox MCP AI 自动测试让开发效率起飞别再纠结学哪个了Java AI 三大框架深度对比Spring AI vs AgentScope-JavaMySQL MCP Server 从零安装到使用实战AI 直接查询数据库Spring 注入三剑客Resource、Autowired、RequiredArgsConstructor 到底该用哪个Spring Event 用了三年同事一句话把我问懵了Java 抽象类Abstract Class彻底讲透从基础到多态实战Spring AI Alibaba 多智能体Multi-agent实战6 大协作模式 完整代码Spring AI Alibaba 智能体作为工具实战别再让主 Agent 当人肉路由器了 粉丝福利免费获取方式私信空门技术栈领取源码包。有任何问题也欢迎后台私信门主看到都会回复~ 对文章内容有疑问想看下一篇写什么主题直接在评论区留言你的每一条评论我都会看。说不定下一篇文章的主题就来自你的问题 项目合作 / 技术咨询平时工作之余也会接一些技术项目和咨询主要方向⚔️企业级开发Java / Spring Boot 项目开发与重构微服务架构设计与落地系统性能调优、线上问题排查AI 应用落地这是我最近的主力方向Spring AI Alibaba / RAG / Agent 应用开发企业私有知识库搭建AI能力接入现有业务系统大模型本地化部署与调优️技术顾问 / 疑难Bug排查项目架构评审与方案设计线上疑难问题定位解决技术选型与团队培训如果你正遇到以下情况欢迎找我聊聊✅ 想做AI项目但技术方案拿不准✅ 项目卡在某个Bug上很久团队搞不定✅ 想把AI接入现有业务不知道从哪下手✅ 需要靠谱的开发外包或长期技术顾问联系渠道按回复速度排序最快私信空门技术栈邮件2929119150qq.com请注明来意和具体需求一个人踩坑是事故一群人踩坑就是《避坑宝典》。—— IT 空门与诸君共修技术大道

相关新闻

最新新闻

日新闻

周新闻

月新闻