觀察者模式以及tomcat實現的觀察者模式 原

觀察者設計模式

觀察者模式也稱作發佈訂閱模式,監聽器模式,被觀察管理各個觀察者,當被觀察者的狀態有變更的時候,會主動通知觀察者。

         通常的情況下,我們會怎麼實現如果一個對象的狀態變更,通知到對相應狀態感興趣的類呢,這個可以分爲主動通知和被動通知。

         主動通知:當實體的狀態有變更,然後主動的通知到觀察者我的狀態變更了,觀察者根據相應的狀態實現自己的操作。

         被動通知:實體狀態變更,不主動通知,然後觀察者定時的去掃描目標狀態,這種操作比較耗費資源,並且不能做到實時,所以一般都不採用這種方式。

模式結構

具體觀察者模式是如何實現的呢?

首先會有一個目標對象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();
            }
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章