观察者模式 前言 观察者模式是一个非常重要的模式,虽然在实际工作中可能很少去直接自己去构造一个观察者模式,并不像单例模式、工厂模式、模板方法模式这样。直接硬编码写出来。但是我们肯定会用到它的补充形式的。比如发布-订阅、监听器、Spring的事件驱动模式。因为毕竟观察者模式直接使用有时候不能达到我们的解耦要求。所以才有了上面的这些常用的东西。
为什么使用观察者模式 在软件系统中,当一个对象行为依赖另外一个对象的状态时。如果不是使用观察者模式的通用结构。则只能在另一个线程中不停的监听查看对象所需要的依赖。在一个复杂的系统中,可能会开启很多线程来实现这一功能,会严重浪费系统的性能。观察者模式的意义也在于此,它可以在单线程中,使某一对象,及时得知自身所依赖的状态的变化,根据其变化做出来相应的动作。
简单的观察者模式 观察者模式主要分为四个角色
角 色
作 用
主题接口
指被观察者对象。当其状态发生改变,它会将这个变化通知给观察者,它维护了观察者所需要依赖的状态
具体主题
具体主题实现了主题接口
观察者接口
观察者接口定义了观察者的基本方法。当依赖状态发生改变时,主题接口就会调用观察者模式的方法
具体观察者
具体观察者实现了观察者接口
我们来看一下代码的基本实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface IObserver { void update (Event event) ; } public interface ISubject { void addObserver (IObserver server) ; void delObserver (IObserver server) ; void inform () ; } public class SubjectImpl implements ISubject { Vector<IObserver> observers = new Vector<>(); public void addObserver (IObserver server) { observers.add(server); } public void delObserver (IObserver server) { observers.removeElement(server); } public void inform () { Event event = new Event(); for (IObserver server : observers){ server.update(evt); } } } public class ObserverImpl implements IObserver { public void update (Event event) { log.info("接受到消息!" ); } }
上面这段代码就是一个简单的实现。可以看出来。其实不复杂。就是简单的状态在需要通知的时候主题接口调用一下通知接口即可。
JDK中的观察者模式 JDK已经为我们准备了一套现有的观察者模式的实现,我们可以直接使用它即可,在java.util
包中,包括Observable和Observer接口
在看一些代码的实现,这里我们以书本订阅的模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Slf 4jpublic class PaperListener implements Observer { @Override public void update (Observable o, Object arg) { log.info("收到书了:{}" ,arg); } } public class Bookstore extends Observable { public static void main (String[] args) throws InterruptedException { PaperListener paperListener1 = new PaperListener(); PaperListener paperListener2 = new PaperListener(); Bookstore bookstore = new Bookstore(); bookstore.addObserver(paperListener1); bookstore.addObserver(paperListener2); Thread.sleep(1000 ); bookstore.setChanged(); bookstore.notifyObservers("西游记" ); bookstore.setChanged(); bookstore.notifyObservers("三国演义" ); } }
执行结果
1 2 3 4 21:02:44.360 [main] INFO com.document.observer.PaperListener - 收到书了:西游记 21:02:44.384 [main] INFO com.document.observer.PaperListener - 收到书了:西游记 21:02:44.384 [main] INFO com.document.observer.PaperListener - 收到书了:三国演义 21:02:44.385 [main] INFO com.document.observer.PaperListener - 收到书了:三国演义
其实当我们查看Observable
的源码,也就知道了其实里面和简单的观察者模式是类似的。不过它为了公用性和并发性做了一些改造。我们每次都要去改变一个change变量才能去通知。实际使用中也要继承Observable
来做的。有兴趣的可以去自己使用下
不过看起来还有缺点,比如它是需要继承Class来实现。而Java只能继承一个类。而且它的标记change只有一个。不能做到组合标记
发布-订阅和观察者模式 前面说了发布订阅可以说是观察者模式的一个补充,因为观察者模式的主题和订阅至于两层,里面的订阅者的维护工作是交给主题自己本身来做的,耦合度不太好。所有发布订阅相当于把他拆分成三个成员:主题、订阅者、维护队列者。这样主题和订阅者就做到完全解耦的工作。队列的维护交给一个实际的类或者服务来做。
所有发布-订阅模式比观察者模式有更好的耦合度,但是在设计思想上是统一的
当然如果你把这个模式在扩展下,比如把维护队列的对象扩展成一个服务,那么就是一个简单的消息队列的原型
Spring中的事件驱动模式 这里讲一下Spring中的ApplicationListener,因为它也是观察者模式的一个实现(毕竟挖了坑,要填)。
先看下简单的使用代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class MyEvent extends ApplicationEvent { public MyEvent (Object source) { super (source); } } @Slf 4j@Component public class MyListener implements ApplicationListener <MyEvent > { @Override public void onApplicationEvent (MyEvent event) { log.info("收到消息了:{}" ,event); } } @SpringBootApplication public class WebBaseApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(WebBaseApplication.class , args ) ; run.publishEvent(new MyEvent(new Object())); } }
输出日志:收到消息了:com.duteliang.webbase.listener.MyEvent[source=java.lang.Object@3d4e405e]
其实也是和观察者模式的设计思想是类似的。但是做了一些变化。看下发布的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 protected void publishEvent (Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null" ); ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this , event); if (eventType == null ) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } if (this .earlyApplicationEvents != null ) { this .earlyApplicationEvents.add(applicationEvent); } else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } if (this .parent != null ) { if (this .parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this .parent).publishEvent(event, eventType); } else { this .parent.publishEvent(event); } } }
跟下去看下广播器代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster { @Override public void multicastEvent (final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null ) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } private void doInvokeListener (ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { } } }
看到这里其实广播器SimpleApplicationEventMulticaster
就相等于主题实现。从接口ApplicationEventMulticaster
就可以看到有管理观察者的方法。ApplicationListener
相当于观察者接口。 Spring引进了Event的概念,使其功能更加丰富了而已,调用观察者的时候根据事件类型来通知。
也可以选择异步通知还是同步通知,以及是否忽略错误等功能。
比如@EventListener
就是为此而建立的,可以异步监听消息。更多的功能可以自己去查下资料了!
1 2 3 4 5 6 7 @Component public class OrderEventListener { @EventListener public void handleOrderEvent (OrderEvent event) { System.out.println("我监听到了handleOrderEvent发布的message为:" + event.getMsg()); } }
在开发中如果有需要使用观察者模式,可以推荐Guava 中的EventBus,对其的扩展非常丰富。可以去尝试一下