1、定义
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
2、代码示例
这里以模拟给关注了公众号的微信用户发送更新通知
UML图如下:
/*** 抽象观察者*/public interface Observer {public void update(String message);}
/*** 具体的观察者*/public class WeiChatUser implements Observer{//微信用户名private String userName;public WeiChatUser(String userName) {this.userName = userName;}@Overridepublic void update(String message) {System.out.println(userName + "." + message);}}
/*** 抽象被观察者*/public interface Subject {/*** 增加订阅者* @param observer*/public void attach(Observer observer);/*** 删除订阅者* @param observer*/public void detach(Observer observer);/*** 通知订阅者更新消息*/public void notify(String message);}
/*** 具体观察者* 微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法*/public class SubscriptionSubject implements Subject {//储存订阅公众号的微信用户private List<Observer> weixinUserlist = new ArrayList<Observer>();@Overridepublic void attach(Observer observer) {weixinUserlist.add(observer);}@Overridepublic void detach(Observer observer) {weixinUserlist.remove(observer);}@Overridepublic void notify(String message) {for (Observer observer : weixinUserlist) {observer.update(message);}}}
public class Client {public static void main(String[] args) {SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();//创建微信用户WeixinUser user1=new WeixinUser("杨影枫");WeixinUser user2=new WeixinUser("月眉儿");WeixinUser user3=new WeixinUser("紫轩");//订阅公众号mSubscriptionSubject.attach(user1);mSubscriptionSubject.attach(user2);mSubscriptionSubject.attach(user3);//公众号更新发出消息给订阅的微信用户mSubscriptionSubject.notify("专栏更新了");}}
代码结果输出如下:
杨影枫.专栏更新了月眉儿.专栏更新了紫轩.专栏更新了
3、观察者模式的优缺点
3.1、优点
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系,从而使得各自的变换都不会影响另一边的变换。
- 目标与观察者之间建立了一套触发机制
- 支持广播通信
- 符合“开闭原则”的要求
- 可替换性:
- 使用设计模式的目的之一是使该类成为可复用的组件
- 一是被观察者无需在观察自己的是谁,只用他们实现了Observer接口接口即可,这些实例都通过addObserver方法注册的,所以一定可以调用他们的update方法(即多态);
- 观察者同理
3.2、缺点
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
4、应用
4.1、JDK中的观察者模式
观察者模式在 Java 语言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们构成了 JDK 对观察者模式的支持(可以去查看下源码,写的比较严谨)。但是在 Java9中 被弃用了。
4.2、Spring中的观察者模式
Spring 事件驱动模型也是观察者模式很经典的应用。就是我们常见的项目中最常见的事件监听器。
Spring 中观察者模式的四个角色
- 事件:ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。
Spring 也为我们提供了很多内置事件,ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。 - 事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。
- 事件源:ApplicationContext,
ApplicationContext是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在ApplicationEventPublisher中定义了事件发布的方法:publishEvent(Object event) - 事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
代码示例
//事件@Componentpublic class MyEvent extends ApplicationEvent {public MyEvent(ApplicationContext source) {super(source);System.out.println("MyEvent 构造器执行");}public void echo() {System.out.println("模拟业务逻辑执行");}}
spring中事件监听实现的两种方式:1、实现ApplicationListener接口;2、使用 @EventListener
//事件监听@Componentpublic class MyListenerA implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent myEvent) {System.out.println("MyListenerA");myEvent.echo();}}
@Componentpublic class MyListenerB {@EventListenerpublic void onApplicationEvent(MyEvent myEvent) {System.out.println("MyListenerB");myEvent.echo();}}
@Componentpublic class MyPublisher implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}/*** 发布事件* 监听该事件的监听者都可以获取消息** @param myEvent*/public void publisherEvent(MyEvent myEvent) {System.out.println("---开始发布 myEvent 事件---");applicationContext.publishEvent(myEvent);}}
//单元测试@RunWith(SpringRunner.class)@SpringBootTestpublic class ApplicationTests {@Autowiredprivate MyPublisher myPublisher;@Autowiredprivate MyEvent myEvent;@Testpublic void contextLoads() {myPublisher.publisherEvent(myEvent);}}
5、总结
个人理解观察者模式就是,好比所有要接收通知的人都在我(事件发布)这里留下手机号,不管是移动,联通还是电信也好,都是11位的(实现Observer接口,符合特定的规范),有新的通知了就可以根据手机号通知所有人。
