觀察者模式

認識觀察者模式

《Head First 設計模式》非常通俗易懂,生動活潑的向我們展示了,各種設計模式,同樣觀察者模式,也使用《Head First 設計模式》中的小栗子,來解釋什麼是觀察者模式。

先看一下報社和訂戶的栗子:

  1. 報社的業務就是出版報紙。

  2. 向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。只要你是他們的訂戶,你就會一直收到新報紙。

  3. 當你不想再看報紙的時候,取消訂閱,他們就不會再送新報紙來。

  4. 只要報社還在運營,就會一直有人(或單位)向他們訂閱報紙或取消訂閱報紙。

出版者 + 訂閱者 = 觀察者模式

我們把名稱改一下,報社即出版者改爲主題Subject,訂戶即訂閱者改爲觀察者Observer

那如何定義觀察者模式呢?當然你也可以使用報社的例子來比對適合觀察者模式的場景。

觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。觀察者模式屬於行爲型模式。

類圖:
觀察者模式基本圖

  • Subject(interface):這是主題接口,對象使用此接口註冊爲觀察者,或者把自己從觀察者中刪除。
  • ConcreteSubject(class):一個具體主題總是實現主題接口,除了註冊和撤銷方法之外,具體主題還實現了notifyObservers()方法,此方法用於在狀態改變時更新所有當前觀察者。
  • Observer(interface):所有潛在的觀察者必須實現觀察者接口,這個接口只有update()一個方法, 當主題狀態改變時它被調用。
  • ConcreteObserver(class):具體的觀察者可以是實現Observer接口的任意類。觀察者必須註冊具體主題,以便接收更新。

觀察者模式的好處(鬆耦合)

當兩個對象之間鬆耦合,它們依然可以交互,但是不太清楚彼此的細節。

觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。

爲什麼呢?

關於觀察者的一切,主題只知道觀察者實現了某個接口,也就是Observer接口。主
題不需要知道觀察者的具體類是誰、做了些什麼或其他任何細節。任何時候我們都可增加新的觀察者。因爲主題唯一依賴的東西是一個實現Observer接口的對象列表,所以我們可以隨時增加觀察者。

事實上,在運行時我們可以用新的觀察者取代現有的觀察者,主題不會受到任何影響。同樣的,也可以在任何時候刪除某些觀察者。有新類型的觀察者出現時,主題的代碼不需要修改。假如我們有個新的具體類需要當觀察者,我們不需要爲了兼容新類型而修改主題的代碼,所有要做的就是在新的類裏實現此觀察者接口,然後註冊爲觀察者即可。主題不在乎別的,它只會發送通知給所有實現了觀察者接口的對象。我們可以獨立地複用主題或觀察者。如果我們在其他地方需要使用主題或觀察者,可以輕易地複用,因爲二者並非緊耦合。

改變主題或觀察者其中一方,並不會影響另一方。因爲兩者是鬆耦合的,所以只要他
們之間的接口仍被遵守,我們就可以自由地改變他們。

設計原則

爲了交互對象之間的鬆耦合設計而努力。

鬆耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,
是因爲對象之間的互相依賴降到了最低。

針對接口編程,不針對實現。

多用組合少用繼承

具體實現

接下來要實現一個,生活中的觀察者模式。

當然觀察者可能只有一個,也可能只有一個。

比如說你第一次去你的女朋友家裏去,七大姑八大姨就是觀察者Observer,你就是被觀察者也就是Subject。這裏只以父母舉個栗子。

類結構:
類結構

Subject 被觀察者主題接口

//被觀察者主題接口
public interface Subject {
    //註冊成爲觀察者
    void registObserver(Observer o);
    //反註冊移除觀察者
    void unRegistObserver(Observer o);
    //通知所有觀察者變化
    void notifyAllObservers();
}

Observer 觀察者接口

//觀察者接口
public interface Observer {
    void update(int score);//接收分數
}

Evaluate 評價接口

//評價接口
public interface Evaluate {
    void evaluate(int score);//根據接受的分數對你進行評價
}

You (主題)被觀察者的實現類

//(主題)被觀察者的實現類
public class You implements Subject {

    private ArrayList<Observer> observers;
    private int score;

    You() {
        observers = new ArrayList<Observer>();
    }

    public void registObserver(Observer o) {
        if (o != null) {
            observers.add(o);
        }
    }

    public void unRegistObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

    public void yourBehavior(int whatBehaviorScore) {
        this.score = whatBehaviorScore;
        notifyAllObservers();
    }

    public void notifyAllObservers() {
        for (Observer o : observers) {
            o.update(score);
        }
    }
}

YourGirlFriendFather觀察者的實現類 你女朋友的爸爸

//觀察者的實現類 你女朋友的爸爸
public class YourGirlFriendFather implements Observer ,Evaluate{

    private Subject you;
    private int score;

    YourGirlFriendFather(Subject you) {
        this.you = you;
        you.registObserver(this);
    }

    public void update(int score) {
        this.score = score;
        evaluate(score);
    }

    public void evaluate(int score) {
      int fatherScore = score + 20;
        switch (fatherScore) {
            case 20:
                System.out.println("father:還不錯有待考察");
                break;
            case 80:
                System.out.println("father:問問他家長啥時候見個面,這小夥子不錯");
                break;
            case 100:
                System.out.println("father:跟他家長要個號碼,直接訂婚");
                break;
            case 120:
                System.out.println("father:你倆先去,領結婚證");
                break;
        }
    }
}

YourGirlFriendMother 觀察者的實現類 你女朋友的媽媽

//觀察者的實現類 你女朋友的媽媽
public class YourGirlFriendMother implements Observer,Evaluate {

    private Subject you;
    private int score;

    YourGirlFriendMother(Subject you) {
        this.you = you;
        you.registObserver(this);
    }

    public void update(int score) {
        this.score = score;
        evaluate(score);
    }

    public void evaluate(int score) {
        int motherScore = score + 25;
        switch (motherScore) {
            case 25:
                System.out.println("mother:還行,比較傻,說話還不甜");
                break;
            case 85:
                System.out.println("mother:挺溫柔,對你很不錯,會說話");
                break;
            case 105:
                System.out.println("mother:居家好男人,掙錢多,又高又帥");
                break;
            case 125:
                System.out.println("mother:有房有車,有掙錢,脾氣又好,又高又帥,說話甜");
                break;
        }
    }
}

TestObserver 你女朋友的父母觀察你的言行舉止對你進行評價(觀察者模式測試)

//觀察者模式測試
public class TestObserver {
    public static void main(String[] args) {
        You you = new You();

        YourGirlFriendFather father = new YourGirlFriendFather(you);
        YourGirlFriendMother mother = new YourGirlFriendMother(you);

        you.yourBehavior(0);
        you.yourBehavior(60);
        you.yourBehavior(80);
        you.yourBehavior(100);
    }
}

運行打印:

father:還不錯有待考察
mother:還行,比較傻,說話還不甜
father:問問他家長啥時候見個面,這小夥子不錯
mother:挺溫柔,對你很不錯,會說話
father:跟他家長要個號碼,直接訂婚
mother:居家好男人,掙錢多,又高又帥
father:你倆先去,領結婚證
mother:有房有車,有掙錢,脾氣又好,又高又帥,說話甜

觀察者模式在實際應用中的缺點和優點總結

優點

  • 觀察者和被觀察者之間是抽象耦合,應對業務變化能力強,可擴展性強;
  • 觀察者模式支持廣播通訊。被觀察者會向所有的登記過的觀察者發出通知。

同時觀察者模式也有一些缺點

  • 觀察者模式有時會有開發效率和運行效率的問題,程序中包括一個被觀察者和多個觀察者時,調試和開發會相對變得複雜;
  • 觀察者如果數量很多,所有的觀察者都通知到會花費一定時間;
  • 觀察者模式中被觀察者通知觀察者時默認是順序執行,所以當一個觀察者執行緩慢或者卡頓就會影響整體的執行效率,如果有這種情況最好採用異步執行的方式去處理;
  • 如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰,在使用觀察者模式時要特別注意這一點。

參考

《Head First 設計模式》

23種設計模式(8):觀察者模式

java/android 設計模式學習筆記目錄

觀察者模式(ObserverPattern)

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