觀察者模式的項目實踐

業(yè)務場景

項目是一個單頁面web應用乳愉,有一個基礎的websocket服務励翼,用于和服務器通信咸灿。剛開始主要有兩個作用:

  1. 賬號防重復登陸野建。當賬號在另外的地方登陸時,websocket收到服務器消息纪铺,網站即時彈窗提示用戶“當前賬號在另外一個地方登陸”,然后網站清除登錄信息并跳轉到登錄頁面。
  2. 當打開送貨管理界面時铣鹏,若有新的送貨請求,websocket收到服務器消息哀蘑,即時提示用戶“您有新的送貨請求吝沫,請及時處理”呻澜。若沒有打開送貨管理界面,不提示惨险。

起初是這樣的實現的:

//BasciWebsocket.js
 websocket.onmessage = function (event) {
   try{
        let {data} = event;
        let _data = JSON.parse(data);
        switch(_data.type){
          case 100:
            Modal.warning({
              title: '賬號登錄提醒',
              content: "您的賬號已經在另外的地方進行登陸,請確認是否您本人操作羹幸。如有異常,請聯(lián)系公司系統(tǒng)管理員。",
              okText: '知道了',
              onOk: ()=> {
                tool.clearUserCookie();
                window.location = "/login";
              }
            });
            break;
          case 101:
            let isPageShow = tool.ifDeliveryPageShow();
            if(isPageShow){
               //提示“您有新的送貨請求辫愉,請及時處理”
            }
            break;
        }
      }catch(e){

      }
 };

看起來并沒有什么問題栅受。但是,隨著項目越來越復雜恭朗,websocket負責通知的消息也越來越豐富屏镊。接收到消息后,往往還要改變具體的頁面展現痰腮,跟具體的頁面的關聯(lián)性非常大而芥。比如,用戶停留在訂單詳情頁時膀值,如果收到新的訂單反饋棍丐,那么訂單詳情頁的評論列表區(qū)域就要動畫出現新的反饋信息。

這個時候沧踏,你會發(fā)現歌逢,把這類跟具體頁面強關聯(lián)的邏輯寫在基礎的BasciWebsocket.js文件里不科學,BasciWebsocket.js也變得很臃腫翘狱,維護起來有些費勁秘案。問題就在于基礎的websocket服務與每個頁面的消息處理邏輯不夠解耦。

不難看出潦匈,在這個業(yè)務場景里阱高,websocket服務其實就是一個消息發(fā)布者,而這些具體頁面則是消息訂閱者茬缩。很自然地讨惩,我想到了一根救命稻草——經典的發(fā)布-訂閱模式(又叫觀察者模式)。

觀察者模式

觀察者模式定義對象間的一種一對多的依賴關系寒屯,當一個對象的狀態(tài)發(fā)生改變時荐捻,所有依賴它的對象都將得到通知。

如何實現觀察者模式寡夹?

  1. 指定好誰充當發(fā)布者(websocket服務)处面;
  2. 為發(fā)布者添加一個緩存列表,用于存放回調函數以便通知訂閱者(具體頁面)菩掏;
  3. 發(fā)布者對外提供消息訂閱/退訂方法魂角,實質就是改變緩存列表的內容;
  4. 發(fā)布消息時智绸,發(fā)布者會遍歷這個緩存列表野揪,一次觸發(fā)里面存放的訂閱者回調函數访忿;

實踐

首先,把BasciWebsocket.js變身為消息發(fā)布者:

export default class BasicWebsocket{

  constructor() {
    if(typeof BasicWebsocket.instance === 'object') {
      return BasicWebsocket.instance;
    }
    //緩存列表
    this.listeners = {};
    this.init();
    
    BasicWebsocket.instance = this;
  }

  //消息訂閱
  addListener = (key,func)=>{
     this.listeners[key] = func;
  };

  //取消訂閱
  removeListener = (key)=>{
     delete this.listeners[key];
  };


  init = ()=>{
    //獲取websocket對象,全局唯一
    let websocket = this.getWebsocket(); 

    let that = this;
    //監(jiān)聽websocket消息
    websocket.onmessage = function (event) {
      try{
        let {data} = event
        let _data = JSON.parse(data);
        //遍歷緩存列表, 通知消息訂閱者
        let keys = Object.keys(that.listeners);
        for(let i = 0,j = keys.length; i < j; i++){
          let func = that.listeners[keys[i]];
          func(_data);
        }
      }catch(e){

      }
    };
  };
  
  //其他代碼
}

然后斯稳,在發(fā)貨管理頁加載時海铆,可以進行發(fā)貨消息的訂閱;發(fā)貨管理頁卸載時挣惰,進行消息退訂卧斟。其他頁面如此類推。

class Distribution extends Component{
  // 構造
  constructor(props) {
    super(props);
    // 初始狀態(tài)
    this.state = {
      newCount:0
    };
    this.websocket = new BasicWebsocket();
  }

  //組件加載完畢
  componentDidMount() {
    let that = this; 
    //消息訂閱
    this.websocket.addListener("distribution",(d)=>{
      if(d.type == 101){
        that.showNotification(); //提示“您有新的送貨請求憎茂,請及時處理”
        //在頁面展示新的發(fā)貨請求數量
        let {count} = d.data;
        let count = that.state.newCount + count;
        that.setState({
          newCount:count
        });
      }
    });
  }

  //組件即將卸載
  componentWillUnmount() {
    //消息退訂
    this.websocket.removeListener("distribution");
  }
}

至此珍语,通過觀察者模式實現了基礎websocket服務和具體頁面邏輯的解耦,代碼可維護性得到提高竖幔。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末板乙,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子拳氢,更是在濱河造成了極大的恐慌募逞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饿幅,死亡現場離奇詭異,居然都是意外死亡戒职,警方通過查閱死者的電腦和手機栗恩,發(fā)現死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洪燥,“玉大人磕秤,你說我怎么就攤上這事∨踉希” “怎么了市咆?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長再来。 經常有香客問我蒙兰,道長,這世上最難降的妖魔是什么芒篷? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任搜变,我火速辦了婚禮,結果婚禮上针炉,老公的妹妹穿的比我還像新娘挠他。我一直安慰自己,他們只是感情好篡帕,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布殖侵。 她就那樣靜靜地躺著贸呢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拢军。 梳的紋絲不亂的頭發(fā)上楞陷,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音朴沿,去河邊找鬼猜谚。 笑死,一個胖子當著我的面吹牛赌渣,可吹牛的內容都是我干的魏铅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼坚芜,長吁一口氣:“原來是場噩夢啊……” “哼览芳!你這毒婦竟也來了?” 一聲冷哼從身側響起鸿竖,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沧竟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缚忧,有當地人在樹林里發(fā)現了一具尸體悟泵,經...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年闪水,在試婚紗的時候發(fā)現自己被綠了糕非。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡球榆,死狀恐怖朽肥,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情持钉,我是刑警寧澤衡招,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站每强,受9級特大地震影響始腾,放射性物質發(fā)生泄漏。R本人自食惡果不足惜空执,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一窘茁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脆烟,春花似錦山林、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桑孩。三九已至,卻和暖如春框冀,著一層夾襖步出監(jiān)牢的瞬間流椒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工明也, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宣虾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓温数,卻偏偏與公主長得像绣硝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子撑刺,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,160評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理鹉胖,服務發(fā)現,斷路器够傍,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 點擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網易云信 SDK 為 Web 應用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,766評論 0 15
  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品甫菠,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式冕屯。簡單...
    舟漁行舟閱讀 7,764評論 2 17
  • 今天的閱讀材料是《零秒思考》寂诱,讀了兩遍才讀真正讀懂。 第一遍看的時候安聘,印象最深的就是拿一張A4紙痰洒,橫放,寫標題搞挣,寫...
    烏卓閱讀 423評論 1 5