Java 設(shè)計模式 -- 觀察者模式

學(xué)校里有個十分可愛的女孩子性锭,很多男孩都紛紛加入了她的粉絲行列赠潦,大家爭先恐后的去搜集她的信息。這個時候女孩的室友草冈,同學(xué)都開始向她抱怨她奥,她也深感愧疚,感覺麻煩了周圍的朋友怎棱。于是哩俭,她決定公開自己的狀態(tài),前提是不再去麻煩她身邊的人拳恋。開始凡资,她公開自己每天吃飯和學(xué)習(xí)的狀態(tài),只要她的粉絲在她這里進行注冊诅岩,就可以不費吹灰之力的收到自己的動態(tài)信息讳苦,這對于粉絲而言簡直是個天大的好消息...這個時候一個程序員路過,看到了這個現(xiàn)象吩谦,靈光一閃鸳谜,這是觀察者模式。首先式廷,將女孩抽象成一個類咐扭,她的行為我們也可以寫成方法,于是有了下面這兩個類。

public interface Girl {
    public void addFans(Fans fans);

    public void removeFnas(Fans fans);

    public void notifyFans();
}

public class LovelyGirl implements Girl {
    private String eatState = "Inexact";
    private String studyState = "Inexact";
    private ArrayList<Fans> fanses;

    public LovelyGirl() {
        fanses = new ArrayList<>();
    }

    @Override
    public void addFans(Fans fans) {
        fanses.add(fans);
    }

    @Override
    public void removeFnas(Fans fans) {
        int i = fanses.indexOf(fans);
        if (i >= 0) {
            fanses.remove(i);
        }
    }

    @Override
    public void notifyFans() {
        for (int i = 0; i < fanses.size(); i++) {
            Fans fans = fanses.get(i);
            fans.update(eatState, studyState);
        }
    }

    public void setEatState(String eatState) {
        this.eatState = eatState;
        notifyFans();
    }

    public void setStudyState(String studyState) {
        this.studyState = studyState;
        notifyFans();
    }
}

這里蝗肪,將女孩寫成了一個接口袜爪。畢竟,人總會見異思遷的嘛薛闪,萬一學(xué)校里來了個很漂亮的女孩子呢辛馆?那恐怕就要多出一個 BeautyGirl 類了。講完了女孩豁延,我們也得講講那些粉絲吧昙篙,粉絲有很多,他們的目的也只有一個 ——獲取自己心儀女孩的最新信息诱咏。那好吧苔可,為了讓你們和女神保持一致,也為你們寫一個接口吧袋狞。

public interface Fans {
    public void update(String eatState, String studyState);
}

有了接口之后焚辅,眾多的粉絲們就可以實現(xiàn)這個接口,去獲得最新信息了苟鸯。比如說粉絲甲和粉絲乙——

public class FansOne implements Fans {

    @Override
    public void update(String eatState, String studyState) {
        System.out.println("I am fans one : " + eatState + ", " + studyState);
    }
}
public class FansTwo implements Fans {

    @Override
    public void update(String eatState, String studyState) {
        System.out.println("I am fans two : " + eatState + ", " + studyState);
    }
}

好了同蜻,所有準(zhǔn)備工作已經(jīng)完成了,不過這個時候已經(jīng)中午了倔毙,女孩說了埃仪,我要開始吃飯了,比如像下面這樣

public class Client {
    public static void main(String[] args) {
        LovelyGirl girl = new LovelyGirl();

        FansOne fansOne = new FansOne();
        girl.addFans(fansOne);

        FansTwo fansTwo = new FansTwo();
        girl.addFans(fansTwo);

        girl.setEatState("I am eating");
    }
}

打印結(jié)果如下

I am fans one : I am eating, Inexact
I am fans two : I am eating, Inexact

其實陕赃,上面這么一個例子就是我們常說的觀察者模式卵蛉。觀察者模式定義了對象之間的一對多依賴,當(dāng)一個對象狀態(tài)改變時么库,它的所有依賴者都會收到通知并且自動更新傻丝。生活中很多地方用到這種模式,比如說天氣預(yù)報訂閱系統(tǒng)诉儒,報紙訂閱系統(tǒng)等葡缰。

上面這個例子還有可以修改之處,因為我們只寫了推送功能忱反,如果粉絲們想主動獲取信息呢泛释?如果有的粉絲對你的學(xué)習(xí)狀態(tài)不感興趣并且表示再也不想收到關(guān)于學(xué)習(xí)的信息推送了。那么該怎么進行改進呢温算?正好借這個機會怜校,我們來看看 Java 為我們內(nèi)置的觀察者模式。Java 為我們提供了 Observer 接口和 Observable 類注竿,下面我們來介紹一下如何使用茄茁。

  • 如何把對象變成觀察者 魂贬?
    實現(xiàn)觀察者接口,然后調(diào)用 Observable 對象的 addObserver() 方法裙顽。不在想當(dāng)觀察者對象的時候付燥,調(diào)用 deleteObserver() 方法就可以了。

  • 可觀察者如何送出通知 愈犹?
    首先键科,需要繼承 java.util.Observable 類產(chǎn)生可觀察者類,然后需要兩個步驟漩怎。

    1. 先調(diào)用 setChanged() 方法萝嘁,標(biāo)記狀態(tài)已經(jīng)改變的事實。
    2. 然后調(diào)用兩種 notifyObservers() 方法中的一個 : notifyObservers() 或者notifyObservers(Object arg)
  • 觀察者如何接受通知扬卷?
    觀察者實現(xiàn)了更新的方法,但是方法的簽名不太一樣 : update(Observable o, Object arg)酸钦。 這個時候就可以實現(xiàn)兩種不同的效果怪得。如果我們將 Object 屬性不指定,也就是只傳遞 Observable 對象的值卑硫,這個時候就是默認(rèn)不推送給觀察者任何信息徒恋,不過觀察者可以通過被觀察者提供的 get 方法主動獲取消息(這里也暗示了一個信息,就是我們的觀察者內(nèi)部需要獲得被觀察者的引用)欢伏。如果這里我們指定了 Object 的對象入挣,那么被觀察者會將信息推送給觀察者。

注意哦硝拧,以上我們提到的 update() 方法中的 Object 參數(shù)和被觀察者中notifyObservers(Object o) 中的 Object 是對應(yīng)的径筏。

利用以上介紹的信息,去改寫以上的代碼障陶。

public class LovelyGirl extends Observable {
    private String eatState = "Inexact";
    private String studyState = "Inexact";

    public void setEatState(String eatState) {
        this.eatState = eatState;

    }

    public void setStudyState(String studyState) {
        this.studyState = studyState;
    }

    public String getEatState() {
        return eatState;
    }

    public String getStudyState() {
        return studyState;
    }

    public void stateChanged() {
        setChanged();
        notifyObservers();
    }
}

可見滋恬,在這里我們使用了無參數(shù)的 notifyObservers() 方法,這也就意味著抱究,我們采用了不主動推送的方式恢氯。還有這里的 setChanged(),是 Observable 類中實現(xiàn)的一個方法鼓寺,這個方法的作用是標(biāo)記狀態(tài)已經(jīng)改變的事實勋拟,只有調(diào)用了這個函數(shù),被觀察者才會將最新的狀態(tài)通知觀察者妈候。

public class FansOne implements Observer {

    private Observable observable;

    public FansOne(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof LovelyGirl) {
            LovelyGirl girl = (LovelyGirl) o;
            System.out.println("eatState : " + girl.getEatState() +
                    " studyState : " + girl.getStudyState());
        }
    }
}

可見敢靡,這里我們就將 addObserver() 方法寫到了這里,因為我們既然在觀察者中引用了被觀察者對象州丹,也就沒有必要將添加的方法放入主函數(shù)了醋安。這個時候再看看主函數(shù)的代碼以及輸出結(jié)果

public class Client {
    public static void main(String[] args) {
        LovelyGirl girl = new LovelyGirl();
        FansOne fansOne = new FansOne(girl);

        girl.setEatState("I am eating...");
    }
}


eatState : I am eating... studyState : Inexact

可見杂彭,利用了Java 內(nèi)置的觀察者模式之后,我們自己動手寫的代碼量就就減少了許多吓揪,而且邏輯也更加清晰了亲怠。不過我感覺 Java 內(nèi)部將 Observable 設(shè)置為一個類還是有缺點的,因為Java 并不允許多重繼承柠辞,這就限制了 Observable 的復(fù)用能力~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末团秽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子叭首,更是在濱河造成了極大的恐慌习勤,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焙格,死亡現(xiàn)場離奇詭異图毕,居然都是意外死亡,警方通過查閱死者的電腦和手機眷唉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門予颤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冬阳,你說我怎么就攤上這事蛤虐。” “怎么了肝陪?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵驳庭,是天一觀的道長。 經(jīng)常有香客問我氯窍,道長饲常,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任荞驴,我火速辦了婚禮不皆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘熊楼。我一直安慰自己霹娄,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布鲫骗。 她就那樣靜靜地躺著犬耻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪执泰。 梳的紋絲不亂的頭發(fā)上枕磁,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音术吝,去河邊找鬼计济。 笑死茸苇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沦寂。 我是一名探鬼主播学密,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼传藏!你這毒婦竟也來了腻暮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤毯侦,失蹤者是張志新(化名)和其女友劉穎哭靖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侈离,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡试幽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卦碾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抡草。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蔗坯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情燎含,我是刑警寧澤宾濒,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站屏箍,受9級特大地震影響绘梦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赴魁,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一卸奉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颖御,春花似錦榄棵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芦岂,卻和暖如春瘪弓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禽最。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工腺怯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袱饭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓呛占,卻偏偏與公主長得像虑乖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子栓票,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容