参考:Spring 中的观察者模式 设计模式(五)观察者模式

1、定义

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

2、代码示例

这里以模拟给关注了公众号的微信用户发送更新通知
UML图如下:
observer.svg

  1. /**
  2. * 抽象观察者
  3. */
  4. public interface Observer {
  5. public void update(String message);
  6. }
  1. /**
  2. * 具体的观察者
  3. */
  4. public class WeiChatUser implements Observer{
  5. //微信用户名
  6. private String userName;
  7. public WeiChatUser(String userName) {
  8. this.userName = userName;
  9. }
  10. @Override
  11. public void update(String message) {
  12. System.out.println(userName + "." + message);
  13. }
  14. }
  1. /**
  2. * 抽象被观察者
  3. */
  4. public interface Subject {
  5. /**
  6. * 增加订阅者
  7. * @param observer
  8. */
  9. public void attach(Observer observer);
  10. /**
  11. * 删除订阅者
  12. * @param observer
  13. */
  14. public void detach(Observer observer);
  15. /**
  16. * 通知订阅者更新消息
  17. */
  18. public void notify(String message);
  19. }
  1. /**
  2. * 具体观察者
  3. * 微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法
  4. */
  5. public class SubscriptionSubject implements Subject {
  6. //储存订阅公众号的微信用户
  7. private List<Observer> weixinUserlist = new ArrayList<Observer>();
  8. @Override
  9. public void attach(Observer observer) {
  10. weixinUserlist.add(observer);
  11. }
  12. @Override
  13. public void detach(Observer observer) {
  14. weixinUserlist.remove(observer);
  15. }
  16. @Override
  17. public void notify(String message) {
  18. for (Observer observer : weixinUserlist) {
  19. observer.update(message);
  20. }
  21. }
  22. }
  1. public class Client {
  2. public static void main(String[] args) {
  3. SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
  4. //创建微信用户
  5. WeixinUser user1=new WeixinUser("杨影枫");
  6. WeixinUser user2=new WeixinUser("月眉儿");
  7. WeixinUser user3=new WeixinUser("紫轩");
  8. //订阅公众号
  9. mSubscriptionSubject.attach(user1);
  10. mSubscriptionSubject.attach(user2);
  11. mSubscriptionSubject.attach(user3);
  12. //公众号更新发出消息给订阅的微信用户
  13. mSubscriptionSubject.notify("专栏更新了");
  14. }
  15. }

代码结果输出如下:

  1. 杨影枫.专栏更新了
  2. 月眉儿.专栏更新了
  3. 紫轩.专栏更新了

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 中观察者模式的四个角色

  1. 事件:ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。
    Spring 也为我们提供了很多内置事件,ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent
  2. 事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。
  3. 事件源:ApplicationContextApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法:publishEvent(Object event)
  4. 事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

代码示例

  1. //事件
  2. @Component
  3. public class MyEvent extends ApplicationEvent {
  4. public MyEvent(ApplicationContext source) {
  5. super(source);
  6. System.out.println("MyEvent 构造器执行");
  7. }
  8. public void echo() {
  9. System.out.println("模拟业务逻辑执行");
  10. }
  11. }

spring中事件监听实现的两种方式:1、实现ApplicationListener接口;2、使用 @EventListener

  1. //事件监听
  2. @Component
  3. public class MyListenerA implements ApplicationListener<MyEvent> {
  4. @Override
  5. public void onApplicationEvent(MyEvent myEvent) {
  6. System.out.println("MyListenerA");
  7. myEvent.echo();
  8. }
  9. }
  1. @Component
  2. public class MyListenerB {
  3. @EventListener
  4. public void onApplicationEvent(MyEvent myEvent) {
  5. System.out.println("MyListenerB");
  6. myEvent.echo();
  7. }
  8. }
  1. @Component
  2. public class MyPublisher implements ApplicationContextAware {
  3. private ApplicationContext applicationContext;
  4. @Override
  5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6. this.applicationContext = applicationContext;
  7. }
  8. /**
  9. * 发布事件
  10. * 监听该事件的监听者都可以获取消息
  11. *
  12. * @param myEvent
  13. */
  14. public void publisherEvent(MyEvent myEvent) {
  15. System.out.println("---开始发布 myEvent 事件---");
  16. applicationContext.publishEvent(myEvent);
  17. }
  18. }
  1. //单元测试
  2. @RunWith(SpringRunner.class)
  3. @SpringBootTest
  4. public class ApplicationTests {
  5. @Autowired
  6. private MyPublisher myPublisher;
  7. @Autowired
  8. private MyEvent myEvent;
  9. @Test
  10. public void contextLoads() {
  11. myPublisher.publisherEvent(myEvent);
  12. }
  13. }

5、总结

个人理解观察者模式就是,好比所有要接收通知的人都在我(事件发布)这里留下手机号,不管是移动,联通还是电信也好,都是11位的(实现Observer接口,符合特定的规范),有新的通知了就可以根据手机号通知所有人。