观察者设计模式
观察者模式也称作发布订阅模式,监听器模式,被观察管理各个观察者,当被观察者的状态有变更的时候,会主动通知观察者。
通常的情况下,我们会怎么实现如果一个对象的状态变更,通知到对相应状态感兴趣的类呢,这个可以分为主动通知和被动通知。
主动通知:当实体的状态有变更,然后主动的通知到观察者我的状态变更了,观察者根据相应的状态实现自己的操作。
被动通知:实体状态变更,不主动通知,然后观察者定时的去扫描目标状态,这种操作比较耗费资源,并且不能做到实时,所以一般都不采用这种方式。
模式结构
具体观察者模式是如何实现的呢?
首先会有一个目标对象Target,这个是被观察者主体,它持有一个观察者列表,观察者均会实现观察者接口中的通知方法,以便接收通知。当观察者状态有变更时,会循环持有的观察者列表,然后调用其通知方法。
现在我们模拟一个场景,来用观察者模式来实现,场景是家里面有一套智能家庭影院,当我们打开观影模式时,此时家里窗帘自动关闭,屋内的灯光自动调整为昏暗色,然后音响调整为环绕声。当家庭影院从观影模式调到正常模式时,窗帘拉开,屋内灯光打开,音响调整到立体声。
实现
手工实现
首先我们有一个家庭影院的主体,然后有一些周边的设备,智能窗帘,智能音响和灯光。代码实现如下:
package design.observer;/** * Created by wangtengfei1 on 2017/9/13. * 家庭影院的模式,分别是放映模式和正常模式,实际上也就是事件 */public enum EventMode { VIDEO,NORMAL}
package design.observer;import java.util.ArrayList;import java.util.List;/** * Created by wangtengfei1 on 2017/9/13. * 家庭影院主体 */public class HomeVideo { EventMode eventMode;//观影模式 //注册的观察者列表 ListobserverLists = new ArrayList (); //增加观察者 public void addObserver(HomeVideoObserver observer){ observerLists.add(observer); } //删除观察者 public void deleteObserver(HomeVideoObserver observer){ observerLists.remove(observer); } //通知观察者 public void notifyObserver(EventMode eventMode){ for(HomeVideoObserver observer:observerLists){ observer.update(this.eventMode); } } //家庭影院开关 public void turn(EventMode eventMode){ this.eventMode = eventMode; notifyObserver(eventMode); }}
package design.observer;/** * Created by wangtengfei1 on 2017/9/13. */public interface HomeVideoObserver { public void update(EventMode eventMode);}package design.observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能音响 */public class Audio implements HomeVideoObserver { @Override public void update(EventMode eventMode) { if(eventMode==EventMode.NORMAL){ System.out.println("\t>>>立体声模式播放"); } if (eventMode ==EventMode.VIDEO){ System.out.println("\t>>>环绕声模式播放"); } }}package design.observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能灯泡 */public class Bulb implements HomeVideoObserver { @Override public void update(EventMode eventMode) { if(eventMode==EventMode.NORMAL){ System.out.println("\t>>>灯光调亮"); } if (eventMode ==EventMode.VIDEO){ System.out.println("\t>>>灯光调暗"); } }}package design.observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能窗帘 */public class WindowCurtains implements HomeVideoObserver { @Override public void update(EventMode eventMode) { if(eventMode==EventMode.NORMAL){ System.out.println("\t>>>窗帘开启"); } if (eventMode ==EventMode.VIDEO){ System.out.println("\t>>>窗帘关闭"); } }}
遥控器,也就是客户端
package design.observer;/** * Created by wangtengfei1 on 2017/9/13. * 遥控器 */public class RemoteControl { private static final HomeVideo homeVideo = new HomeVideo(); public static void main(String[] args) throws Exception { HomeVideoObserver bulb = new Bulb(); HomeVideoObserver windowCurtains = new WindowCurtains(); HomeVideoObserver audio = new Audio(); homeVideo.addObserver(bulb); homeVideo.addObserver(windowCurtains); homeVideo.addObserver(audio); System.out.println(">>遥控器按下播放按钮"); homeVideo.turn(EventMode.VIDEO); Thread.sleep(100); System.out.println(">>100后影片播放完毕"); homeVideo.turn(EventMode.NORMAL); }}
输出如下
JDK实现
由于观察者模式太常用了,所以jdk在工具类中实现了一个观察者,只需要我们实现Observer接口,并且实现其update方法即可。现在用jdk自带的观察者模式改造一下下面的程序。
package design.jdkObserver;import design.observer.EventMode;import design.observer.HomeVideoObserver;import java.util.ArrayList;import java.util.List;import java.util.Observable;import java.util.Observer;/** * Created by wangtengfei1 on 2017/9/13. */public class HomeVideo extends Observable { public EventMode eventMode;//观影模式 //家庭影院开关 public void turn(EventMode eventMode){ this.eventMode = eventMode; this.setChanged(); super.notifyObservers(); }}
package design.jdkObserver;import design.observer.EventMode;import java.util.Observable;import java.util.Observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能音响 */public class Audio implements Observer { public Audio() { } public Audio(Observable observable) { observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof HomeVideo) { HomeVideo video = (HomeVideo) o; if (video.eventMode == EventMode.NORMAL) { System.out.println("\t>>>立体声模式播放"); } if (video.eventMode == EventMode.VIDEO) { System.out.println("\t>>>环绕声模式播放"); } } }}package design.jdkObserver;import design.observer.EventMode;import design.observer.HomeVideoObserver;import java.util.Observable;import java.util.Observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能灯泡 */public class Bulb implements Observer { public Bulb(){} public Bulb(Observable observable){ observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof HomeVideo) { HomeVideo video = (HomeVideo) o; if(video.eventMode==EventMode.NORMAL){ System.out.println("\t>>>灯光调亮"); } if (video.eventMode ==EventMode.VIDEO){ System.out.println("\t>>>灯光调暗"); } } }}package design.jdkObserver;import design.observer.EventMode;import design.observer.HomeVideoObserver;import java.util.Observable;import java.util.Observer;/** * Created by wangtengfei1 on 2017/9/13. * 智能窗帘 */public class WindowCurtains implements Observer { public WindowCurtains(){} public WindowCurtains(Observable observable){ observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof HomeVideo) { HomeVideo video = (HomeVideo) o; if(video.eventMode==EventMode.NORMAL){ System.out.println("\t>>>窗帘开启"); } if (video.eventMode ==EventMode.VIDEO){ System.out.println("\t>>>窗帘关闭"); } } }}
jdk方式的观察者模式的遥控器,客户端
package design.jdkObserver;import design.observer.EventMode;import java.util.Observer;/** * Created by wangtengfei1 on 2017/9/13. */public class JdkRemoteControl { public static void main(String[] args) throws Exception { HomeVideo homeVideo = new HomeVideo(); Observer audio = new Audio(homeVideo); Observer bulb = new Bulb(homeVideo); Observer windowCurtains = new WindowCurtains(homeVideo); //或者采用下面的这种方式,是等效的 /** * Observer audio = new Audio(); * homeVideo.addObserver(audio); */ System.out.println(">>遥控器按下播放按钮"); homeVideo.turn(EventMode.VIDEO); Thread.sleep(100); System.out.println(">>100后影片播放完毕"); homeVideo.turn(EventMode.NORMAL); }}
输出结果和上面以后,就不在贴出了,可以看出,jdk实现的更简洁,并且安全性也更高。
tomcat实现
tomcat在监听容器声明周期事件的时候也采用了观察者模式,但是tomcat并没有采用jdk实现的观察者模式,而是自己实现了一套观察者模式,因为它需要不同的监听器,包括对声明周期的监听,对容器的监听,这样能更加契合它本身的业务。看一下tomcat是怎么实现的。
首先观察对象要实现LifecycleListener接口的LifecycleEvent方法。这个方法接收一个LifecycleEvent对象,来标明此时发生了什么事儿。
而监听对象则是LifecycleBase类,这个类提供了addLifecycleListener、findLifecycleListeners、removeLifecycleListener、fireLifecycleEvent这些方法,分别是增加监听器,查找监听器,移除监听器和通知各个监听器这些方法。调用通知的方法在什么地方呢,就是在于容器状态改变时,例如Server启动时,在StandardServer在调用start方法启动,然后方法内调到startInternal方法中,会传递一个Configure_start事件,然后调用fireLifecycleEvent通知到各个监听者
public interface LifecycleListener { /** * Acknowledge the occurrence of the specified event. * * @param event LifecycleEvent that has occurred */ public void lifecycleEvent(LifecycleEvent event);}
org.apache.catalina.core. StandardServer监听器通知方式 fireLifecycleEvent
@Override protected void startInternal() throws LifecycleException { fireLifecycleEvent(CONFIGURE_START_EVENT, null); setState(LifecycleState.STARTING); globalNamingResources.start(); // Start our defined Services synchronized (servicesLock) { for (int i = 0; i < services.length; i++) { services[i].start(); } } }