淺談設計模式2——觀察模式


我想大家一定都去醫(yī)院掛過號,大概流程是:掛號柱告,到就診室門口排隊截驮,等著醫(yī)生叫號看病。今天說的觀察者模式就是這樣一個道理际度。很多的病人就是觀察者葵袭,等待醫(yī)生狀態(tài)發(fā)生改變——看完病,叫號乖菱。然后所有的病人聽到號坡锡,看看叫的是不是自己,走入診室看病窒所,或者繼續(xù)等待鹉勒。

1、背景敘述
依然感覺還是用書上的例子比較好吵取,但是像“氣象觀測站”這種東西禽额,除了看天氣預報,我絕不會想到皮官。為了不讓問題更加復雜脯倒,我想還是直接切入主題,用比較形式化的描述方式來闡述臣疑,也能更快進入主題盔憨。

2、觀察者模式
對于某個“對象A”讯沈,當它的狀態(tài)發(fā)生變化時——某個屬性發(fā)生變化郁岩,或者調用某個函數。則其他的對象也因為“對象A”的變化做出某些操作缺狠∥噬鳎“對象A”為被監(jiān)聽者,其他的對象為監(jiān)聽者挤茄。有沒有想到Java中的事件如叼?對,這個就是事件的原型穷劈。先結合訂報紙的示例笼恰,用語言描述一下具體實現踊沸,然后上代碼,最后給大家說下這樣做有啥好處社证。

1)訂報紙需要以下步驟
(1)去郵局逼龟,請求訂報紙,郵局將我們的個人信息注冊追葡∠俾桑——郵局就是“被監(jiān)聽者”,我們是“監(jiān)聽者”宜肉。監(jiān)聽者需要到被監(jiān)聽者那里注冊個人信息匀钧。
(2)當郵局到了新報紙,則就將報紙分發(fā)給我們谬返≈梗——郵局從沒有報紙的狀態(tài),轉換到有報紙的狀態(tài)遣铝,狀態(tài)發(fā)生改變吊圾,需要告訴我們狀態(tài)發(fā)生改變,“報紙”就是“被監(jiān)聽者”發(fā)送給“監(jiān)聽者”信息
(3)我們收到報紙后翰蠢,有的人讀報紙项乒,了解新聞;有的人比較土豪梁沧,用來包家具檀何;有的人更土豪,送給鄰居廷支∑导——“監(jiān)聽者”收到消息后,采取不同的動作恋拍。

從以上分析舉例來看垛孔,監(jiān)聽者模式(也就是觀察者模式,監(jiān)聽者說的比較順口)其實是一種通信模式施敢。就是將信息進行廣播周荐。然后收到相同信息的不同實體,根據信息和自身特點僵娃,做出相應操作概作。
需要注意的是,因為投遞員只認識綠皮郵筒默怨,所以所有的監(jiān)聽者要提供統(tǒng)一的監(jiān)聽接口來接受消息讯榕。監(jiān)聽接口其實就是個函數。
但是,為了保證程序面向對象的良好設計愚屁,我們使用接口來對各個類進行修飾济竹。

2)程序部分

import java.util.ArrayList;
//測試類
public class Test
{
    public static void main(String args[])
    {
        PostOffice postOffice=new PostOffice();//被觀察者
        Observer1 observer1=new Observer1();//觀察者1號
        Observer2 observer2=new Observer2();//觀察者2號
        
        postOffice.addObserver(observer1);//注冊觀察者1號
        postOffice.addObserver(observer2);//注冊觀察者2號
        
        postOffice.setState(true, true);//改變被觀察者狀態(tài)
    }
}
/*
如上面的程序,監(jiān)聽者需要跑到被監(jiān)聽者那里去注冊霎槐,只有注冊了规辱,被監(jiān)聽者才能發(fā)消息給監(jiān)聽者。
*/


interface Subject //定義被監(jiān)聽者接口栽燕,但是有一個不好點,下面會仔細說改淑。
{
    public void addObserver(Observer o);
    public void deleteObserver(Observer o);
    public void notifyObserver(Object otherArg);
}

interface Observer//定義監(jiān)聽者接口碍岔,實際只是定義一個接受信息的接口 
{
    public void  receive(Subject subject, Object otherArg );
}

//實現被被觀察者接口
class PostOffice implements Subject
{
    private boolean newsPaper=false;
    private boolean gift=false;
    
    private ArrayList<Observer> arrayList;//存放觀察者的列表
    
    public PostOffice()//構造函數
    {
        this.newsPaper=false;
        this.gift=false;
        this.arrayList=new ArrayList<Observer>(); //注意一定要初始化對象實體
    }
    
      
    public void stateChanged()//用戶自定義
    {
        this.notifyObserver(null);
    }
    
    public void setState(boolean newspaper, boolean gift) //用戶自定義
    {
        this.newsPaper=newspaper;
        this.gift=gift;
        
        this.stateChanged();
    }
    
    //以下為實現的Subject接口
    public void addObserver(Observer o)
    {
        this.arrayList.add(o);
    }
    public void deleteObserver(Observer o)
    {
        int index=this.arrayList.indexOf(o);
        if(index>0)
            this.arrayList.remove(index);
    }
    public void notifyObserver(Object otherArg) 
    {
        for (Observer observer : this.arrayList) 
            observer.receive(this, otherArg);
    }
}

class Observer1 implements Observer
{
    @Override
    public void receive(Subject subject, Object otherArg) 
    {
        System.out.println("this is Observer1");        
    }
}

class Observer2 implements Observer
{

    @Override
    public void receive(Subject subject, Object otherArg) {
        System.out.println("this is Observer2");
        
    }
}

3)程序分析部分
希望上面的程序大家能看懂,下面主要針對程序需要說明幾點注意事項朵夏。
(1)Java類庫中蔼啦,被監(jiān)聽者Subject實際上是一個類,而此處是一個接口仰猖。那么到底哪個比較好呢捏肢?
如果是接口,就必須維護一個ArrayList列表(接口中都是public成員饥侵,這樣就暴露了列表成員)鸵赫,來存儲申請注冊的監(jiān)聽者。而如果是類躏升,則沒有這么多麻煩辩棒,直接調用addObserver(),添加監(jiān)聽者即可膨疏。也就是說一睁,接口破壞了封裝性。
但是佃却,如果是類者吁,由于Java只支持單繼承,因此這樣做子類就不能繼承其他類饲帅,也不好用复凳。

(2)Observer接口中,receive()函數傳遞的參數可以根據需要自己設定灶泵,但是需要考慮兩個方面:
a染坯、參數不能太多,否則會增加通信量丘逸,嚴重影響程序的執(zhí)行效率单鹿。
b、參數不能太少深纲,因為這個函數是所有類接收信息的統(tǒng)一接口仲锄,一旦未來功能要擴展劲妙,就需要修改很多的東西——凡是實現receive()方法的類全都需要修改。
所以此處我把接口設計成接收兩個參數儒喊,一個是被監(jiān)聽者镣奋,一個是其他類。如果需要被監(jiān)聽者的成員變量怀愧,可以采用getter 和 setter方法侨颈。如果需要獲取其他信息,可以將這些信息全部打包在一個容器類里芯义,通過otherArg來傳遞哈垢。

(3)對于PostOffice類,有三個函數:setState()函數來改變當前對象的狀態(tài)扛拨,stateChanged()函數由用戶自定義耘分,來執(zhí)行當對象狀態(tài)改變時,需要作出的動作绑警。最后一個notifyObserver()才是真正通知監(jiān)聽者求泰,通知他們。為什么要這么麻煩计盒,直接將notifyObserver()放到setState()中不是更好渴频?
這樣做將:改變對象狀態(tài),改變對象狀態(tài)后應該做的操作北启,通知監(jiān)聽者枉氮,三個操作進行了分離。將操作的粒度變小暖庄,從而讓修改更加容易聊替。

同時,還要注意培廓,對于郵局這個模型惹悄,報紙來了通知一下就好。但是對于溫度檢測等模型肩钠,變化速度太快泣港,而響應速度又不太高(每秒變化一度,但是變化十度才做出反應)這樣的情況价匠,設置上面的三個函數就很有效当纱。

順便說下,上面的函數依然有文章可做踩窖∑侣龋可以在Subject接口中再增加一個setchanged()函數。下面只是說下增加到PostOffice中的代碼。增加了setChanged()箫柳,好處是可以根據當前狀態(tài)手形,來決定到底通不通知監(jiān)聽者。

       private boolean changed=false;
       public void stateChanged()//用戶自定義
    {
        this.setChanged(false);//注意這行悯恍,通知完后库糠,就表明沒有改變了
        this.notifyObserver(null);
    }
    
    public void setState(boolean newspaper, boolean gift) //用戶自定義
    {
        this.newsPaper=newspaper;
        this.gift=gift;
        
        this.setChanged(true);//注意這行,發(fā)生了改變
        
        this.stateChanged();
    }
       public void  setChanged(boolean changed)
    {
        this.changed=changed;
    }

至此涮毫,觀察模式結束瞬欧。碼了這么多字,好累罢防。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末艘虎,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子篙梢,更是在濱河造成了極大的恐慌,老刑警劉巖美旧,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渤滞,死亡現場離奇詭異,居然都是意外死亡榴嗅,警方通過查閱死者的電腦和手機妄呕,發(fā)現死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗽测,“玉大人绪励,你說我怎么就攤上這事∵胫啵” “怎么了疏魏?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晤愧。 經常有香客問我大莫,道長,這世上最難降的妖魔是什么官份? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任只厘,我火速辦了婚禮,結果婚禮上舅巷,老公的妹妹穿的比我還像新娘羔味。我一直安慰自己,他們只是感情好钠右,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布赋元。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪们陆。 梳的紋絲不亂的頭發(fā)上寒瓦,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音坪仇,去河邊找鬼杂腰。 笑死,一個胖子當著我的面吹牛椅文,可吹牛的內容都是我干的喂很。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼皆刺,長吁一口氣:“原來是場噩夢啊……” “哼少辣!你這毒婦竟也來了?” 一聲冷哼從身側響起羡蛾,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤漓帅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痴怨,有當地人在樹林里發(fā)現了一具尸體忙干,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年浪藻,在試婚紗的時候發(fā)現自己被綠了捐迫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡爱葵,死狀恐怖施戴,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情萌丈,我是刑警寧澤赞哗,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站辆雾,受9級特大地震影響懈玻,放射性物質發(fā)生泄漏。R本人自食惡果不足惜乾颁,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一涂乌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧英岭,春花似錦湾盒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毅人。三九已至,卻和暖如春尖殃,著一層夾襖步出監(jiān)牢的瞬間丈莺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工送丰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缔俄,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓器躏,卻偏偏與公主長得像俐载,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子登失,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容

  • 1 場景問題# 1.1 訂閱報紙的過程## 來考慮實際生活中訂閱報紙的過程遏佣,這里簡單總結了一下,訂閱報紙的基本流程...
    七寸知架構閱讀 4,621評論 5 57
  • 設計模式匯總 一揽浙、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用状婶、多...
    MinoyJet閱讀 3,947評論 1 15
  • 從三月份找實習到現在,面了一些公司馅巷,掛了不少膛虫,但最終還是拿到小米、百度令杈、阿里走敌、京東碴倾、新浪逗噩、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,246評論 11 349
  • 我們經常會遇到驗證碼短信的應用場景,比如:賬戶注冊手機號碼驗證僧须,賬戶認證纲刀、手機綁定、找回密碼担平、支付驗證等示绊。偶爾會碰...
    Mr唐先生閱讀 1,699評論 0 0
  • 伊莉貝莎還是13歲的模樣,穿著有點蓬蓬的公主裙暂论,頭上帶著12顆鉆石的頭夾面褐,帶著香味的手套把她如蔥白樣的小手呵護著...
    柏木白閱讀 482評論 0 0