Java設計模式--觀察者模式

觀察者模式【Observer Pattern 】

觀察者模式以秦國時期李斯監視韓非子爲例。

先看下類圖:

這裏寫圖片描述

被觀察者:

public interface IHanFeiZi {
    //韓非子也是人,也要吃早飯的
    public void haveBreakfast();
    //韓非之也是人,是人就要娛樂活動
    public void haveFun();
}

被觀察者的實現類:

public class HanFeiZi implements IHanFeiZi{
    //韓非子是否在吃飯,作爲監控的判斷標準
    private boolean isHaveBreakfast = false;
    //韓非子是否在娛樂
    private boolean isHaveFun = false;
    //韓非子要吃飯了
    public void haveBreakfast(){
    System.out.println("韓非子:開始吃飯了...");
    this.isHaveBreakfast =true;
    }
    //韓非子開始娛樂了,古代人沒啥娛樂,你能想到的就那麼多
    public void haveFun(){
    System.out.println("韓非子:開始娛樂了...");
    this.isHaveFun = true;
    }
    //以下是bean的基本方法,getter/setter,不多說
    public boolean isHaveBreakfast() {
    return isHaveBreakfast;
    }
    public void setHaveBreakfast(boolean isHaveBreakfast) {
    this.isHaveBreakfast = isHaveBreakfast;
    }
    public boolean isHaveFun() {
    return isHaveFun;
    }
    public void setHaveFun(boolean isHaveFun) {
    this.isHaveFun = isHaveFun;
    }
}

觀察者的接口:

public interface ILiSi {
    //一發現別人有動靜,自己也要行動起來
    public void update(String context);
}

觀察者的實現類:

public class LiSi implements ILiSi{
    //首先李斯是個觀察者,一旦韓非子有活動,他就知道,他就要向老闆彙報
    public void update(String str){
    System.out.println("李斯:觀察到韓非子活動,開始向老闆彙報了...");
    this.reportToQiShiHuang(str);
    System.out.println("李斯:彙報完畢,秦老闆賞給他兩個蘿蔔吃吃...\n");
    }
    //彙報給秦始皇
    private void reportToQiShiHuang(String reportContext){
    System.out.println("李斯:報告,秦老闆!韓非子有活動了--->"+reportContext);
    }
}

全部搞好上面了,就要來個監測程序了。

class Watch extends Thread{
    private HanFeiZi hanFeiZi;
    private LiSi liSi;
    private String type;
    //通過構造函數傳遞參數,我要監控的是誰,誰來監控,要監控什麼
    public Watch(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){
    this.hanFeiZi =_hanFeiZi;
    this.liSi = _liSi;
    this.type = _type;
    }
    @Override
    public void run(){
    while(true){
    if(this.type.equals("breakfast")){ //監控是否在吃早餐
    //如果發現韓非子在吃飯,就通知李斯
    if(this.hanFeiZi.isHaveBreakfast()){
    this.liSi.update("韓非子在吃飯");
    //重置狀態,繼續監控
    this.hanFeiZi.setHaveBreakfast(false);
    }
    }else{//監控是否在娛樂
    if(this.hanFeiZi.isHaveFun()){
    this.liSi.update("韓非子在娛樂");
    this.hanFeiZi.setHaveFun(false);
    }
    } 
    }
    }
}

這樣子是可以完成對韓非子的監測,但仔細想想,就不行了,這樣子while(true)開啓死循環來進行監測,服務器肯定是受不了的,而且監測的對象只有一個,你以爲你是秦始皇。。。還是大出血的修改一下好:韓非子是大人物,肯定會有很多間諜監視的,於是觀察者應該提煉出來,觀察者要觀察的也不只是人家的吃法娛樂吧,故也提煉出來:

這裏寫圖片描述

觀察者:

public interface Observable {
    //增加一個觀察者
    public void addObserver(Observer observer);
    //刪除一個觀察者,——我不想讓你看了
    public void deleteObserver(Observer observer);
    //既然要觀察,我發生改變了他也應該用所動作——通知觀察者
    public void notifyObservers(String context);
}

被觀察者實現上面的接口:

public class HanFeiZi implements Observable{
    //定義個變長數組,存放所有的觀察者
    private ArrayList<Observer> observerList = new ArrayList<Observer>();
    //增加觀察者
    public void addObserver(Observer observer){
    this.observerList.add(observer);
    }
    //刪除觀察者
    public void deleteObserver(Observer observer){
        this.observerList.remove(observer);
    }
    //通知所有的觀察者
    public void notifyObservers(String context){
        for(Observer observer:observerList){
        observer.update(context);
        }
    }
    //韓非子要吃飯了
    public void haveBreakfast(){
        System.out.println("韓非子:開始吃飯了...");
        //通知所有的觀察者
        this.notifyObservers("韓非子在吃飯");
    }
    //韓非子開始娛樂了,古代人沒啥娛樂,你能想到的就那麼多
    public void haveFun(){
        System.out.println("韓非子:開始娛樂了...");
        this.notifyObservers("韓非子在娛樂");
    }
}

再看觀察者:

public interface Observer {
    //一發現別人有動靜,自己也要行動起來
    public void update(String context);
}

然後觀察者來實現這個接口即可,有多少個觀察者都行。

在主類中:

    //三個觀察者產生出來
    Observer liSi = new LiSi();
    Observer wangSi = new WangSi();
    Observer liuSi = new LiuSi();
    //定義出韓非子
    HanFeiZi hanFeiZi = new HanFeiZi();
    //我們後人根據歷史,描述這個場景,有三個人在觀察韓非子
    hanFeiZi.addObserver(liSi);
    hanFeiZi.addObserver(wangSi);
    hanFeiZi.addObserver(liuSi);

    //然後這裏我們看看韓非子在幹什麼
    hanFeiZi.haveBreakfast();

這樣就完成了監視的過程。當然JDK 中提供了:
java.util.Observable 實現類和 java.util.Observer 接口,於是乎上面寫的那個例子中的
Observable 接口可以改換成 java.util.Observale 實現類了。

下面看一下通用類圖:

這裏寫圖片描述

總結:

使用觀察者模式要考慮的問題:
(1)廣播鏈的問題。如果你做過數據庫的觸發器,你就應該知道有一個觸發器鏈的問題,比如表 A 上寫了一個觸發器,內容是一個字段更新後更新表 B 的一條數據,而表 B 上也有個觸發器,要更新表 C,表 C 也有觸發器…,完蛋了,這個數據庫基本上就毀掉了!我們的觀察者模式也是一樣的問題,一個觀察者可以有雙重身份,即使觀察者,也是被觀察者,這沒什麼問題呀,但是鏈一旦建立,這個邏輯就比較複雜,可維護性非常差,根據經驗建議,在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最多轉發一次(傳遞兩次) ,這還是比較好控制的;

(2)異步處理問題。被觀察者發生動作了,觀察者要做出迴應,如果觀察者比較多,而且處理時間比較長怎麼辦?那就用異步。

舉一個應用場景:

比如你到 ATM 機器上取錢,多次輸錯密碼,卡就會被 ATM吞掉,吞卡動作發生的時候,會觸發哪些事件呢?第一攝像頭連續快拍,第二,通知監控系統,吞卡發生;
第三,初始化 ATM 機屏幕,返回最初狀態,你不能因爲就吞了一張卡,整個 ATM 都不能用了吧,一般前兩
個動作都是通過觀察者模式來完成的。

發佈了46 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章