設計模式-觀察者模式 (發佈/訂閱模型)

1、定義

Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically .

定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。

2、類圖

3、角色說明

 Subject被觀察者

定義被觀察者必須實現的職責,它必須能夠動態地增加、取消觀察者。它一般是抽象類或者是實現類,僅僅完成作爲被觀察者必須實現的職責:管理觀察者並通知觀察者。

Observer觀察者

觀察者接收到消息後,即進行update(更新方法)操作,對接收到的信息進行處理。

ConcreteSubject具體的被觀察者

定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知。

ConcreteObserver具體的觀察者

每個觀察在接收到消息後的處理反應是不同,各個觀察者有自己的處理邏輯。


3、一個觀察者例子

定義一個被觀察者接口,實現這個接口的都是被觀察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 被觀察者接口,實現這個接口的都是被觀察者
 */

public interface CommonObservable {
    //添加一個觀察者,一個被觀察者可以被多個觀察者觀察
    public void addObserver(CommonOberver observable);

    //刪除一個觀察者
    public void deleteObserver(CommonOberver observable);

    //通知觀察者做出行動
    public void notifyObervers(String str);

}

當然被觀察者也有自己特有的方法

/**
 * Created by ZLM on 2018/7/3.
 * Describe 被觀察者自身特有功能
 */

public interface ICustomObservable {
    //被觀察的方法
    public void specialMeyhod();
}

定義一個觀察者接口,實現該接口的都是觀察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 觀察者接口
 */

public interface CommonOberver {
    //一旦發現有情況,就做出行動
    public void doSomething(String str);
}

定義一個被觀察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 定義一個被觀察者
 */

public class ANewOberverable implements ICustomObservable, CommonObservable {

    //存放所有的觀察者
    private ArrayList<CommonOberver> mObervers = new ArrayList<>();

    //觀察者要活動了,也就是我們需要觀察的方法
    @Override
    public void specialMeyhod() {
        this.notifyObervers("被觀察者 : \t" + this.getClass().getSimpleName() + "\t活動了!!!請注意");
    }

    //增加觀察者
    @Override
    public void addObserver(CommonOberver observer) {
        this.mObervers.add(observer);
    }

    //刪除觀察者
    @Override
    public void deleteObserver(CommonOberver observer) {
        this.mObervers.remove(observer);
    }

    //通知觀察者
    @Override
    public void notifyObervers(String str) {
        for (CommonOberver oberver : mObervers) {
            oberver.doSomething(str);
        }
    }
}

定義2個觀察者

/**
 * Created by ZLM on 2018/7/3.
 * Describe 觀察者A
 */

public class OberverA implements CommonOberver {
    @Override
    public void doSomething(String str) {
        System.out.println("觀察者:\t" + this.getClass().getSimpleName() + "\t收到信息:" + str);
    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 觀察者B
 */

public class OberverB implements CommonOberver {
    @Override
    public void doSomething(String str) {
        System.out.println("觀察者:\t" + this.getClass().getSimpleName() + "\t收到信息:" + str);
    }
}

調用:

OberverA oberverA = new OberverA();
OberverB oberverB = new OberverB();
ANewOberverable aNewOberverable = new ANewOberverable();
aNewOberverable.addObserver(oberverA);
aNewOberverable.addObserver(oberverB);
aNewOberverable.specialMeyhod();

輸出:

I/System.out: 觀察者:	OberverA	收到信息:被觀察者 : 	ANewOberverable	活動了!!!請注意
I/System.out: 觀察者:	OberverB	收到信息:被觀察者 : 	ANewOberverable	活動了!!!請注意

4、觀察者模式的優缺點

優點:

--觀察者和被觀察者之間是抽象耦合。不管觀察者還是被觀察者都非常容易擴展。而且在Java中都已經實現的抽象層級的定義,在系統方面更是得心應手。

--建立一套觸發機制。

缺點:

--觀察者模式需要考慮一下開發效率和運行效率問題,一個被觀察者,多個觀察者,開發 和調試就會比較複雜,而且在Java中消息的通知默認是順序執行,一個觀察者卡殼,會影響整體的執行效率。在這種情況下,一般考慮採用異步的方式。多級觸發時的效率更是讓人擔憂,大家在設計時注意考慮。

5、使用場景

--關聯行爲場景。需要注意的是,關聯行爲是可拆分的,而不是“組合”關係。

--事件多級觸發場景。

-- 跨系統的消息交換場景,如消息隊列的處理機制。

6、注意

--廣播鏈問題。根據經驗建議,在一個觀察者模式中最多出現一個對象既是觀察者也是被觀察者,也就是說消息最多轉發一次(傳遞兩次),這還是比較好控制的。

--它和責任鏈模式的最大區別就是觀察者廣播鏈在傳播的過程中消息是隨時更改的,它是由相鄰的兩個節點協商的消息結構;而責任鏈模式在消息傳遞過程中基本上保持消息不可變,如果要改變,也只是在原有的消息上進行修正。

--異步處理問題。被觀察者發生動作了,觀察者要做出迴應,如果觀察者比較多,而且處理時間比較長怎麼辦?那就用異步唄,異步處理就要考慮線程安全和隊列的 問題。

7、擴展

其實,Java中已經給我們提供了可擴展的 觀察者(java.util.Observer) 和 被觀察者(java.util.Observable父類。替換掉上文提到的commonObservable和commonObserve

/**
 * Created by ZLM on 2018/7/3.
 * Describe 定義一個被觀察者
 */

public class ANewOberverable extends Observable implements ICustomObservable {

    //觀察者要活動了,也就是我們需要觀察的方法
    @Override
    public void specialMeyhod() {
        super.setChanged();
        super.notifyObservers("被觀察者:" + this.getClass().getSimpleName() + "活動了!!!請注意");

    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 被觀察者自身特有功能
 */

public interface ICustomObservable {
    //被觀察的方法
    public void specialMeyhod();
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 觀察者
 */

public class OberverA implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("觀察者:" + this.getClass().getSimpleName() + "收到信息" + arg.toString());
    }
}
/**
 * Created by ZLM on 2018/7/3.
 * Describe 觀察者
 */

public class OberverB implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("觀察者:" + this.getClass().getSimpleName() + "收到信息" + arg.toString());
    }
}

調用:

OberverA oberverA = new OberverA();
OberverB oberverB = new OberverB();
ANewOberverable aNewOberverable = new ANewOberverable();
aNewOberverable.addObserver(oberverA);
aNewOberverable.addObserver(oberverB);
aNewOberverable.specialMeyhod();

輸出:

I/System.out: 觀察者:OberverA收到信息被觀察者:ANewOberverable活動了!!!請注意
I/System.out: 觀察者:OberverB收到信息被觀察者:ANewOberverable活動了!!!請注意

8、真實項目中如何使用

-- 觀察者和被觀察者之間的消息溝通

被觀察者狀態改變會觸發觀察者的一個行爲,同時會傳遞一個消息給觀察者,這是正確 的,在實際中一般的做法是:觀察者中的update方法接受兩個參數,一個是被觀察者,一個 是DTO(Data Transfer Object,據傳輸對象),DTO一般是一個純潔的JavaBean,由被觀察者生成,由觀察者消費。當然,如果考慮到遠程傳輸,一般消息是以XML格式傳遞。

--觀察者響應方式

我們這樣來想一個問題,觀察者是一個比較複雜的邏輯,它要接受被觀察者傳遞過來的信息,同時還要對他們進行邏輯處理,在一個觀察者多個被觀察者的情況下,性能就需要提到日程上來考慮了,爲什麼呢?如果觀察者來不及響應,被觀察者的執行時間是不是也會被拉長?那現在的問題就是:觀察者如何快速響應?有兩個辦法:

        一是採用多線程技術,甭管是被觀察者啓動線程還是觀察者啓動線程,都可以明顯地提高系統性能,這也就是大家通常所說的異步架構;

        二是緩存技術,甭管你誰來,我已經準備了足夠的資源給你了,我保證快速響應,這當然也是一種比較好方案,代價就是開發難度很大,而且壓力測試要做的足夠充分,這種方案也就是大家說的同步架構。

--被觀察者儘量自己做主

被觀察者的狀態改變是否一定要通知觀察者呢?不一定吧,在設計的時候要靈活考慮,否則會加重觀察者的處理邏輯,一般是這樣做的,對被觀察者的業務邏輯 doSomething方法實現重載,如增加一個doSomething(boolean isNotifyObs)方法,決定是否通知觀察者,而不是在消息到達觀察者時才判斷是否要消費。



歡迎關注微信公衆號:OpenShare,共同成長,共同進步:














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