原文:https://my.oschina.net/u/2457218/blog/1536680
观察者设计模式
观察者模式也称作发布订阅模式,监听器模式,被观察管理各个观察者,当被观察者的状态有变更的时候,会主动通知观察者。
通常的情况下,我们会怎么实现如果一个对象的状态变更,通知到对相应状态感兴趣的类呢,这个可以分为主动通知和被动通知。
主动通知:当实体的状态有变更,然后主动的通知到观察者我的状态变更了,观察者根据相应的状态实现自己的操作。
被动通知:实体状态变更,不主动通知,然后观察者定时的去扫描目标状态,这种操作比较耗费资源,并且不能做到实时,所以一般都不采用这种方式。
模式结构
具体观察者模式是如何实现的呢?
首先会有一个目标对象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;//观影模式
//注册的观察者列表
List<HomeVideoObserver> observerLists = new ArrayList<HomeVideoObserver>();
//增加观察者
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();
}
}
}
Tomcat中对于观察者模式的一个经典使用就是用来控制组件生命周期的Lifecycle
首先我们将Lifecycle用到的几个类对应到Observer模式中的角色中去:
- LifecycleListener: 对应抽象的Observer
- ServerLifecycleLisener等: 对应具体的Observer
- Lifecycle: 对应抽象的Subject
- StandardServer等: 对应具体的Subject
除此之外还有两个辅助类:
- LifecycleEvent: 可以定义事件类别,对不同的事件可区分处理,更加灵活
- LifecycleSupport: 代理了Subject对于Observers的管理,将这个管理抽出来统一实现,以后如果修改只要修改LifecycleSupport类就可以了,不需要去修改所有的Subject,因为所有具体的Subject对Observers的操作都被代理给了LifecycleSupport类了。