觀察者設計模式java
觀察者模式也稱做發佈訂閱模式,監聽器模式,被觀察管理各個觀察者,當被觀察者的狀態有變動的時候,會主動通知觀察者。apache
一般的狀況下,咱們會怎麼實現若是一個對象的狀態變動,通知到對相應狀態感興趣的類呢,這個能夠分爲主動通知和被動通知。設計模式
主動通知:當實體的狀態有變動,而後主動的通知到觀察者個人狀態變動了,觀察者根據相應的狀態實現本身的操做。tomcat
被動通知:實體狀態變動,不主動通知,而後觀察者定時的去掃描目標狀態,這種操做比較耗費資源,而且不能作到實時,因此通常都不採用這種方式。安全
具體觀察者模式是如何實現的呢?ide
首先會有一個目標對象Target,這個是被觀察者主體,它持有一個觀察者列表,觀察者均會實現觀察者接口中的通知方法,以便接收通知。當觀察者狀態有變動時,會循環持有的觀察者列表,而後調用其通知方法。工具
如今咱們模擬一個場景,來用觀察者模式來實現,場景是家裏面有一套智能家庭影院,當咱們打開觀影模式時,此時家裏窗簾自動關閉,屋內的燈光自動調整爲昏暗色,而後音響調整爲環繞聲。當家庭影院從觀影模式調到正常模式時,窗簾拉開,屋內燈光打開,音響調整到立體聲。this
首先咱們有一個家庭影院的主體,而後有一些周邊的設備,智能窗簾,智能音響和燈光。代碼實現以下:spa
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在工具類中實現了一個觀察者,只須要咱們實現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並無採用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(); } } }