用微信公眾號來分析<觀察者模式>

0.文章

用王者榮耀分析<策略模式>


微信

觀察者模式很好理解,我通過微信公眾號這個簡單的栗子來代入一下苇经。
微信公眾號大家都很熟悉赘理,當你關(guān)注了它,它會在服務(wù)器有更新的時候推送信息給你扇单,當然其他人沒訂閱的情況下是不會收到推送的商模。

這個栗子又像訂報紙一樣,在過去沒有什么媒體的時候蜘澜,大家都通過訂閱報紙或者買報紙來了解新鮮事施流,當然只有你訂閱了報社才會發(fā)報紙給你。這就是典型的 主題-訂閱模式鄙信,也就是我們今天要討論的觀察者模式瞪醋。

1.觀察者模式定義

定啥義啊,寫完了也晦澀難懂装诡,直接理解定報紙和微信公眾號银受,別跟我說你沒玩過微信践盼,也沒訂過報紙……那還是先定一波報紙研究研究吧

發(fā)布-訂閱模式,發(fā)布者發(fā)布信息蚓土,訂閱者獲取信息宏侍,訂閱了就能收到信息,沒訂閱就收不到信息蜀漆。簡單不?

2.結(jié)構(gòu)圖

對于我個人來說看類UML圖其實是看不懂的,我是指在作為小白的情況下是不應(yīng)該投入太多時間研究這個比較抽象的類圖的咱旱,而是應(yīng)直接結(jié)合栗子來研究代碼(當然這是我自己的看法确丢,大家完全可以按照自己的思維來)


UML圖片來自百度百科

當然在這里我也不多解釋這個圖,我們一會直接看代碼

3.角色

結(jié)合我們前面說的栗子吐限,主要角色就是主題(發(fā)布者)和訂閱者

主題:

抽象主題(接口)
具體主題實現(xiàn)(公開類)

訂閱者:

訂閱者抽象(接口)
訂閱者實現(xiàn)(公開類)

角色也分的很清晰鲜侥。

4.Demo

這次我們來寫微信公眾號的栗子,先給微信的栗子诸典,然后最后給一個標準的代碼描函。

按照我們的角色部分所描述的。
我們先實現(xiàn)抽象主題狐粱。

package ObserverPattern.Wechat;

/**
 * 抽象觀察者
 * */
public interface SubjectInterface {
    //關(guān)注動作
    void registerAccount(); 
    //取消關(guān)注
    void removeAccount();
    //通知對象更新
    void notifyObject();
}

這個抽象里面一共有三個方法舀寓,關(guān)注取關(guān)和通知更新
下面我們實現(xiàn)一個公眾號。

package ObserverPattern.Wechat;
import java.util.ArrayList;
/**
 * 實現(xiàn)一個名字叫javaclass的公眾號
 * */
public class JavaClass implements SubjectInterface{
    private ArrayList list;
    public JavaClass() {
        //初始化關(guān)注者管理器
        list = new ArrayList();
    }
    @Override
    public void registerAccount() {     
    }
    @Override
    public void removeAccount() {        
    }
    @Override
    public void notifyObject() {       
    }
}

可以發(fā)現(xiàn)我們現(xiàn)在的JavaClass只是填上了接口內(nèi)的方法肌蜻,但是沒寫內(nèi)容互墓,這是因為我們要先實現(xiàn)關(guān)注者抽象。
下面看我們的關(guān)注者抽象蒋搜。

/**
 * 微信用戶抽象
 * */
public interface WeChatUser {
   void update();
}

關(guān)注者當然是微信用戶篡撵,而他的抽象僅僅添加了一個update方法,用戶公眾號通知用戶豆挽。
現(xiàn)在我們開始修改我們的公眾號抽象育谬,建立與微信用戶的聯(lián)系。

public interface SubjectInterface {
    //關(guān)注動作
    void registerAccount(WeChatUser weChatUser);
    //取消關(guān)注
    void removeAccount(WeChatUser weChatUser);
    //通知對象更新
    void notifyObject();
}

然后修改相應(yīng)的公眾號實現(xiàn)

package ObserverPattern.Wechat;

import ObserverPattern.Normal.MyObserver;

import java.util.ArrayList;

/**
 * 實現(xiàn)一個名字叫javaclass的公眾號
 * */
public class JavaClass implements SubjectInterface{
    private ArrayList list;

    public JavaClass() {
        //初始化關(guān)注者管理器
        list = new ArrayList();
    }


    @Override
    public void registerAccount(WeChatUser weChatUser) {
        list.add(weChatUser);
    }

    @Override
    public void removeAccount(WeChatUser weChatUser) {
        //這里應(yīng)該添加更復(fù)雜null判斷帮哈,為了簡單清楚直接刪掉了膛檀。
        list.remove(weChatUser);
    }

    @Override
    public void notifyObject() {
        for(int i = 0;i < list.size();i++){
            WeChatUser weChatUser = (WeChatUser) list.get(i);
            weChatUser.update();
        }
    }
}

通過list列表來管理訂閱者,然后有信息的時候需要調(diào)用notifyObject()通知用戶更新但汞,暫時先不需要知道怎么調(diào)用的宿刮,我們一會梳理,我們可以知道 weChatUser.update();是用于更新公眾號通知的私蕾,然后實際是公眾號把消息發(fā)送給用戶的僵缺,所以我們最好修改update()方法來傳送信息。

我們聲明一個信息實體踩叭。

package ObserverPattern.Wechat;

public class Message {
    private int id;
    private String message;

    public Message(int id, String message) {
        this.id = id;
        this.message = message;
    }

    public Message() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

然后修改update抽象磕潮。

package ObserverPattern.Wechat;

/**
 * 微信用戶抽象
 * */
public interface WeChatUser {
   void update(Message message);
}

當然在具體的公眾號實現(xiàn)中翠胰,我們也要改一下。

@Override
    public void notifyObject() {
        for(int i = 0;i < list.size();i++){
            WeChatUser weChatUser = (WeChatUser) list.get(i);
            weChatUser.update(message);
        }
    }

那么message在哪自脯?

private Message message;
public void setMessage(Message message) {
        this.message = message;
        notifyObject();
    }

可以給公眾號增加一個message屬性之景,然后添加一個setter,
這樣信息就可以通過我們的update傳送了膏潮。

細心的你一定發(fā)現(xiàn)了一個問題锻狗,我們這里直接調(diào)用了notifyObject();方法,通知更新就可以生效焕参。

萬事俱備轻纪,只欠使用者。

package ObserverPattern.Wechat;

public class WechatUsers implements WeChatUser {
    private String name;
    @Override
    public void update(Message message) {
        System.out.println(name+"收到信息:"+message.getId()+":"+message.getMessage());
    }

    public WechatUsers() {
    }

    public WechatUsers(String name) {
        this.name = name;
    }
}

很簡單叠纷,在實現(xiàn)實體的基礎(chǔ)上重寫了update方法刻帚,在里面打印了信息內(nèi)容。

我們開始測試涩嚣,編寫main方法

package ObserverPattern.Wechat;

public class Main {
    public static void main(String[] args) {
        //生成用戶
        WeChatUser weChatUser = new WechatUsers("123456");
        //生成公眾號
        JavaClass javaclass = new JavaClass();
        //關(guān)注公眾號
        javaclass.registerAccount(weChatUser);
        //公眾號發(fā)消息
        javaclass.setMessage(new Message(1,"新聞"));
        System.out.println("--------");
        WeChatUser weChatUser2 = new WechatUsers("小明");
        javaclass.registerAccount(weChatUser2);
        javaclass.setMessage(new Message(2,"娛樂"));
        System.out.println("--------");
        javaclass.removeAccount(weChatUser);
        javaclass.setMessage(new Message(3,"經(jīng)濟"));
    }
}

看看結(jié)果



大家會發(fā)現(xiàn)崇众,只有在訂閱了之后,并且公眾號有新消息的時候航厚,相應(yīng)公眾號才會收到消息顷歌,當取消關(guān)注的時候,將收不到消息阶淘。

具體流程:

        //1.關(guān)注公眾號
        javaclass.registerAccount(weChatUser);


        //2.公眾號發(fā)消息
        javaclass.setMessage(new Message(1,"新聞"));


       //3.設(shè)置信息衙吩,調(diào)用通知更新方法
       public void setMessage(Message message) {
        this.message = message;
        notifyObject();
        }

       //4.通知更新
        @Override
        public void notifyObject() {
          for(int i = 0;i < list.size();i++){
            WeChatUser weChatUser = (WeChatUser) list.get(i);
            weChatUser.update(message);
        }
    }      

    //5.更新消息
     @Override
    public void update(Message message) {
        System.out.println(name+"收到信息:"+message.getId()+":"+message.getMessage());
    }

這樣我們就實現(xiàn)微信公眾號的關(guān)注功能,當然真正微信不是這么實現(xiàn)的溪窒,哈哈哈坤塞,就不要想了學(xué)會這個就可以搞一個微信公眾號了,這是我們用來舉例子的澈蚌。

5.再次理解觀察者模式

1. 發(fā)布者和訂閱者摹芙,修改其中任何一部分,另一部分不會受到影響宛瞄,這是我們平時所說的松耦合浮禾。

這個我們通過代碼也能看出來,兩個代碼是沒有什么關(guān)系的份汗。

2.JDK中也有自帶的觀察者模式盈电。但是被觀察者是一個類而不是接口,限制了它的復(fù)用能力杯活。

這里我們就不介紹它了匆帚,因為平時實現(xiàn)一個觀察者模式并不是很難。

3.對于觀察者模式旁钧,還是拿公眾號的栗子理解最合適不過吸重,結(jié)合實際場景互拾,公眾號挨個通知用戶的話會出現(xiàn)很多問題,所以直接一下子將消息推送出去(雖然也是通過for循環(huán)來單個通知的嚎幸,但是意義已經(jīng)不同了颜矿。)

最主要的是,公眾號和用戶應(yīng)該是獨立的兩部分(但是矛盾的是嫉晶,由于主題抽象依賴了訂閱者抽象骑疆,所以耦合還是存在的)

6.Android中的觀察者模式

記得在上一篇的策略模式中我們提到了listview的adapter,其中我們通過查看源代碼發(fā)現(xiàn)adapter有使用策略者模式车遂,今天我們依然研究adapter
打開adapter的源代碼(AS中封断,鼠標點擊方法名,按Ctrl鍵可以快速轉(zhuǎn)到定義)


很直白有木有舶担,已經(jīng)寫出observer來了,而且registerDataSetObserver和unregisterDataSetObserver我們都見過彬呻,可以看到adapter是個interface衣陶,同時在adapter的源碼中我們找不到notify方法,那么他在哪呢闸氮?



我們找到了BaseAdapter剪况,這個是abstract類,實現(xiàn)了ListAdapter接口蒲跨,那么ListAdapter接口是抽象自adapter接口的译断,我們不研究他,就可以間接認為BaseAdapter抽象自adapter接口就行或悲,在這里面孙咪,我們看到了notify方法。
DataSetObservable里面管理著所有的訂閱者巡语。
這個就是我們的主題翎蹈,那么訂閱者怎么接收消息呢?

聯(lián)系實際男公,我們平時是用setAdapter方法來設(shè)置adapter的

new ListView(this).setAdapter(baseAdapter);

那我們看一下setAdapter源碼



其中有一句荤堪,注冊代碼,那么我們就可以知道了枢赔,只要setAdapter被調(diào)用了澄阳,那么就代表訂閱者已經(jīng)訂閱了主題。
當主題調(diào)用notify的時候踏拜,(也就是我們平時adapter.notifyDataChanged()的時候),然后消息就會被通知到訂閱者

主題調(diào)用順序:

A.調(diào)用notifyDataSetChanged
 baseAdapter.notifyDataSetChanged();
B.調(diào)用notifyChanged
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
C.調(diào)用onChanged碎赢,通知更新
public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
D.看一下onChanged()
public abstract class DataSetObserver {
    public void onChanged() {
        // Do nothing
    }
     public void onInvalidated() {
        // Do nothing
    }
}

發(fā)現(xiàn)沒有實現(xiàn)。然后我們繼續(xù)看訂閱者执隧。

訂閱者調(diào)用順序(視圖更新部分)
首先我們要明白Listview是作為訂閱者的

A.Listview抽象自AbsListView
public class ListView extends AbsListView {}
B.AbsListView 繼承自AdapterView
public abstract class AbsListView extends AdapterView<ListAdapter>
C.AdapterView內(nèi)部類AdapterDataSetObserver 繼承自DataSetObserver 揩抡,并且覆蓋了onChanged()方法
 class AdapterDataSetObserver extends DataSetObserver {
      @Override
        public void onChanged() {
       } 
}

到此户侥,我們會發(fā)現(xiàn),adapter和listview的發(fā)布訂閱模式連接起來了峦嗤。

7.其他發(fā)布訂閱框架

Eventbus:

作為Anroid事件總線框架蕊唐,eventbus采用發(fā)布訂閱模式來處理信息交互,很符合OO特點烁设。

Mqtt:

mqtt是即時通訊協(xié)議替梨,他的工作模式就是發(fā)布訂閱模式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末装黑,一起剝皮案震驚了整個濱河市副瀑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恋谭,老刑警劉巖糠睡,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疚颊,居然都是意外死亡狈孔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門材义,熙熙樓的掌柜王于貴愁眉苦臉地迎上來均抽,“玉大人,你說我怎么就攤上這事其掂∮突樱” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵款熬,是天一觀的道長深寥。 經(jīng)常有香客問我,道長华烟,這世上最難降的妖魔是什么翩迈? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮盔夜,結(jié)果婚禮上负饲,老公的妹妹穿的比我還像新娘。我一直安慰自己喂链,他們只是感情好返十,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椭微,像睡著了一般洞坑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝇率,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天迟杂,我揣著相機與錄音刽沾,去河邊找鬼。 笑死排拷,一個胖子當著我的面吹牛侧漓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播监氢,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼布蔗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了浪腐?” 一聲冷哼從身側(cè)響起纵揍,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎议街,沒想到半個月后泽谨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡特漩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年隔盛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拾稳。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腊脱,靈堂內(nèi)的尸體忽然破棺而出访得,到底是詐尸還是另有隱情,我是刑警寧澤陕凹,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布悍抑,位于F島的核電站,受9級特大地震影響杜耙,放射性物質(zhì)發(fā)生泄漏搜骡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一佑女、第九天 我趴在偏房一處隱蔽的房頂上張望记靡。 院中可真熱鬧,春花似錦团驱、人聲如沸摸吠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寸痢。三九已至,卻和暖如春紊选,著一層夾襖步出監(jiān)牢的瞬間啼止,已是汗流浹背道逗。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留献烦,地道東北人滓窍。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像仿荆,于是被迫代替她去往敵國和親贰您。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評論 25 707
  • 懷念啊我們的青春啊拢操,昨天在記憶里生根發(fā)芽...... 青春這個話題對于我們一點都委婉锦亦,現(xiàn)在正是我們的...
    來誰古巴咯閱讀 196評論 0 0
  • 1 我真的想寫一首短詩 短到只需要一次擁抱 剩下的結(jié)局就都順理成章 不悲不傷 無風無浪 ...
    李榆閱讀 185評論 13 4
  • 要想學(xué)習(xí)某件事情,那就要下決心去堅持做好令境,猶豫不決的話是成不了成功的人的杠园! 晚安
    沉默之鷹閱讀 182評論 0 0
  • 俗話說的不好:只問耕耘抛蚁,莫問收獲。這句話旨在勸人努力埋頭耕田就好了惕橙,管它最后能收獲多少糧食呢瞧甩。說這句話的老祖宗肯定...
    瞎驢閱讀 499評論 0 0