什么是观察者模式
观察者模式是一种对象行为模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。在观察者模式中,主题是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅并接收通知。
观察者模式的核心角色
- 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
- 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
- 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
- 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。
观察者模式如何实现
需求描述
明朝的锦衣服制度是一种特殊的政治制度,锦衣卫表面是上负责帮助皇帝管理仪仗,实际上是直属皇帝的特务机构,皇帝感觉哪个大臣有安全隐患,都会指挥锦衣卫派上一个眼线进行潜伏,暗中观察其一举一动,一旦有什么不好苗头,潜伏的眼线都会立马向上报告,锦衣卫马上出动进行逮捕、审讯。这一套流程下来,实际上就是观察者模式。如果写一个程序,怎么实现这个过程呢?
实现方法
1、定义一个抽象的观察者,也就是锦衣卫,用来定义具体的职能,如逮捕、审讯等;
/** *锦衣卫 */ public interface JinYiWei { /** * 逮捕 */ void arrest(String something); }
2、定义一个抽象主题,也就是眼线,用来执行添加观察者、删除观察者、通知观察者;
/** * 眼线 */ public abstract class YanXian { public List<JinYiWei> jinYiWeiList = new ArrayList<JinYiWei>(); /** * 添加 * * @param jinYiWei */ public void add(JinYiWei jinYiWei) { this.jinYiWeiList.add(jinYiWei); } /** * 移除 * * @param jinYiWei */ public void remove(JinYiWei jinYiWei) { this.jinYiWeiList.remove(jinYiWei); } public abstract void report(String something); }
3、定义具体的主题,也就是皇帝的大臣,继承于抽象的主题,也就是眼线,大臣不管做什么事,都在被眼线的暗中观察着,并报告上级。
/** * 尚书大人 */ public class ShangShu extends YanXian { private String name; public ShangShu(String name) { this.name = name; } public void doSomething(String something) { System.out.println(this.name + something); //观察者开始工作 this.report(this.name + something); } @Override public void report(String something) { for (JinYiWei jinYiWei : this.jinYiWeiList) { jinYiWei.arrest(something); } } }
4、定义具体的观察者,也就锦衣卫的XX千户、XX 指挥使,用来执行具体的职能任务,如逮捕、审讯等;
/** * 锦衣卫千户大人 */ public class QianHu implements JinYiWei { private String name; public QianHu(String name) { this.name = name; } @Override public void arrest(String something) { System.out.println("报告皇帝:" + something); System.out.println(this.name + "准备执行逮捕任务"); } }
5、客户端
public class Client { public static void main(String[] args) { JinYiWei jinYiWei=new QianHu("锦衣卫的马千户"); ShangShu shangShu = new ShangShu("吏部的李尚书"); shangShu.add(jinYiWei); shangShu.doSomething("写了一首反诗"); ShangShu shangshu2=new ShangShu("兵部的王尚书"); shangshu2.add(jinYiWei); shangshu2.doSomething("买了十副盔甲"); } }
如何扩展
最近朱皇帝收到风声,将军们开始骄奢淫逸了,甚至开始强抢民女了,于派安插眼线,暗中搜集罪证,一旦证据确凿,马上逮捕,怎么实现这个过程呢?
定义具体的主题,也就是皇帝的将军,继承于抽象的主题,也就是眼线,将军们不管做什么事,都在被眼线的暗中观察着,并报告上级。
/** * 将军 */ public class JiangJun extends YanXian{ private String name; public JiangJun(String name) { this.name = name; } public void doSomething(String something){ System.out.println(this.name+something); this.report(this.name+something); } @Override public void report(String something) { for (JinYiWei jinYiWei : jinYiWeiList) { jinYiWei.arrest(something); } } }
public class Client { public static void main(String[] args) { JinYiWei jinYiWei=new QianHu("锦衣卫的马千户"); ShangShu shangShu = new ShangShu("吏部的李尚书"); JiangJun jiangJun = new JiangJun("李将军"); jiangJun.add(jinYiWei); jiangJun.doSomething("纵容部下强抢民女"); } }
观察者模式适用哪些场景
业务场景具有下面的特征就可以使用观察者模式:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面
- 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变
有没有比较具体的业务场景示例呢?当然有,比如:
- 新闻评论功能:在此功能中,用户可以发布或删除留言,并自动追加留言和更新留言条数。为了实现这一功能,可以使用观察者模式。具体来说,留言模块、用户模块和提交模块可以作为被观察对象,而观察者模式可以用于在被观察对象发布消息时自动通知和更新观察者对象(例如,追加留言或更新留言条数)。
- 事件驱动的系统:观察者模式也适用于事件驱动的系统。在这种系统中,当一个事件发生时,需要通知所有注册过的观察者。观察者模式可以提供一种方便的方式来管理和通知所有的观察者。
- 数据变更通知:在某些业务场景中,当数据发生变更时,需要通知相关的用户或系统。观察者模式可以用于在这种数据变更发生时自动通知所有的观察者。
- 消息推送系统:在消息推送系统中,当有新的消息产生时,需要推送给相关的用户。观察者模式可以用于在这种消息发布时自动通知和更新观察者对象。
- 库存管理系统:在库存管理系统中,当库存量发生变化时,需要通知所有关注此变化的用户或系统。比如,当库存量低于某个阈值时,需要自动发送通知给负责补货的部门或人员。
观察者模式在Spring中的应用
- Spring MVC中的控制器(Controller):Spring MVC中的控制器类似于观察者模式中的观察者,它们监听并响应应用程序状态的变化。当应用程序状态发生变化时,控制器会接收到通知并执行相应的操作。
- Spring消息代理(Message Proxy):Spring消息代理可以用于实现异步消息通知。通过代理模式和观察者模式的结合使用,可以在应用程序中实现消息的自动分发和订阅。
- Spring AOP(面向切面编程):Spring AOP可以用于实现切面通知。通过使用观察者模式,可以在程序运行时自动执行切面逻辑,例如日志记录、事务管理等。
- Spring事件系统(Event System):Spring事件系统可以用于实现事件的发布和订阅。通过观察者模式,可以将事件和观察者关联起来,当事件发生时,所有订阅了该事件的观察者都会接收到通知。
- Spring消息队列(Message Queue):Spring消息队列可以用于实现消息的异步处理和消息的分布式发送。通过观察者模式,可以将消息队列和观察者关联起来,当有新消息到达时,所有订阅了该队列的观察者都会接收到通知。
关于观察者模式与监听器模式
从某种角度来说,观察者模式与监听器模式可以理解是一会事,但是从具体的实现细节上来说,还是有一些区别:
- 观察者模式定义了一种一对多的依赖关系,使得当一个主题对象的状态发生变化时,所有依赖它的观察者都会得到通知并自动更新。在这种模式中,主题对象维护了一个观察者列表,并在其状态发生变化时通知所有的观察者。观察者对象不知道其他观察者的存在,它只知道如何更新自己。这种模式更适合于一对多的场景。
- 监听器模式则是一种更适合一对一的通信场景的模式。在监听器模式中,监听器对象注册到时间源对象上,当时间源对象发生变化时,它会通知所有注册过的监听器。监听器对象知道其他监听器的存在,并且可以相互交互。在Springboot中,已经实现了具体的监听器机制,之前输出过两篇文章:
总结
优点
- 观察者模式在被观察者和观察者之间建立一个抽象的耦合,使得它们可以属于不同的抽象化层次。
- 观察者模式支持广播通信,即当被观察者的状态发生变化时,会通知所有的观察者。
- 观察者模式的实现方式符合"开闭原则",即可以在不改变已存在的实现类的情况下,增加新的观察者类。
缺点
- 如果被观察者对象有很多的直接和间接的观察者,那么将所有的观察者都通知到会花费很多时间。
- 如果在被观察者和观察者之间存在循环依赖,那么它们之间会触发循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
总之,观察者模式是一种非常有效的行为型设计模式,但是在使用观察者模式时需要谨慎考虑其优缺点,并确保在实践中能够有效地应用这种设计模式。