JavaScript設計模式之觀察者模式(學習筆記)

設計模式(Design Pattern)對於軟件開發來講其重要性不言而喻,代碼可複用、可維護、可擴展一直都是軟件工程中的追求!對於我一個學javascript的人來講,理解設計模式彷佛有些困難,對僅切圖、作少許交互效果的FE甚至可能不會用到,可是當你開始使用Angular/Backbone等框架的時候,就沒法避免設計模式、MVC/MVVM這些東西了(反正我是傷腦筋)。javascript

我學設計模式是剛開始接觸編程大概三個月的時候,看一本書《大話設計模式》,裏面用C#語言來寫,我很無語,由於強類型的編程語言對於我這種寫弱類型的毛頭小子來講,彷佛又有困難啊,因而我就學C#基礎語法規則去了。。。今年年初我又學了JAVA的基礎語法規則。。。然而個人初衷已經被拋棄在一旁,落上了厚厚的灰層。對於自學編程的我來講,不知道學習編程的前後順序彷佛吃虧很多,可是總要有開頭的!html

以上可直接跳過java


先來講一下我對「觀察者模式」的我的理解:觀察者模式又稱「發佈-訂閱(Publish/Subscribe)模式」,發佈與訂閱顯然是兩個不一樣對象的功能,好比RSS。知乎是一個發佈者(發佈一些對某方面問題的高贊同解答),我做爲一個訂閱者(在個人郵箱裏面訂閱了知乎的相關發佈內容),個人同事以及個人老闆都訂閱了知乎,因此在這個模型中,有一個發佈者,有三個訂閱者。編程

在具體編程中,發佈者有了新的內容,須要向訂閱者推送數據,那麼新的內容(state)、訂閱者有哪些(observers)就是發佈者須要包含的東西,誰訂閱了、誰退訂了則要對發佈者中的訂閱者列表進行更新。如下是發佈者的相關信息代碼解讀:設計模式

//發佈者
        function Publisher(){
            this.observers = [];
            this.state = "";

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;                
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.state);
            };
        }

以上在遍歷observers數組的時候,可使用數組類的filter、forEach等新特性來處理。第三個notice函數表示發佈者有了新東西,而後對訂閱者列表中的全部人通知他們我有新內容(state)了,大家拿去更新大家的郵箱吧。這裏把內容傳遞給了每個訂閱者的update更新功能。數組

那麼訂閱者呢?訂閱者很簡單,只須要具備一個update功能便可(每個訂閱者update可能不同,好比我是放進郵箱了,個人同事則將訂閱的拿來,而且順便把舊的刪掉了,個人上司則將數據轉發到Gmail去了)。下面是訂閱者相關信息代碼解讀:框架

//訂閱者
        function Subscribe(){
            this.update = function(data){
                  console.log(data);
            };
        }

實際上,由於每個訂閱者都有這個update,因此咱們一般應該將其添加到構造器的原型上面,當對這個默認的update功能不知足要求的時候,能夠爲每個訂閱者的實例設置單獨的update,好比將這個data發送給別人。最後我們看看怎麼應用。編程語言

//實際應用
        var oba = new Subscribe(),
            obb = new Subscribe();

        var pba = new Publisher();

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            console.log(state+"hello!");
        }
        obb.update = function(state){
            console.log(state+"world!");
        }
        pba.state = "open ";
        pba.notice();

你們看到,咱們在最後對發佈者手動設置了它的內容(state)而且要求他發出通知(notice)。在實際項目中,發佈者的內容多是從後臺獲取的也多是從前臺某地方輸入的。然而發佈者每次更新內容後又要手動調用通知是否是有點多餘呢?既然更新了內容那就確定要通知別人了啊。那咱們就把內容的更新與發出通知進行綁定好了,看下面的代碼:函數

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        //發佈者
        function Publisher(){
            this.observers = [];
            var state = "";     //讓該內容不能直接訪問

            //新增兩個對於state的操做 獲取/更新
            this.getState=function(){
                return state;
            }
            this.setState=function(value){
                state = value;
                this.notice();
            }

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;                
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.getState()); //獲取發佈者的內容
            };
        }


        //訂閱者
        function Subscribe(){
            this.update = function(data){
                  console.log(data);
            };
        }

        //實際應用
        var oba = new Subscribe(),
            obb = new Subscribe();

        var pba = new Publisher();

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            console.log(state+"hello!");
        }
        obb.update = function(state){
            console.log(state+"world!");
        }
        pba.setState("open "); //發佈者更新了內容
    </script>
</body>
</html>

對於以上的內容,或許並無跟咱們的項目中實際出現的問題有關,那咱們就來代入這種設計模式,作一個例子:三個文本框ABC,其中A可編輯,B與C不可編輯且B的值是A的值加上後綴"@w3c.com",C的值是A的值加上前綴"ID-"。學習

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        <label>用戶名稱:<input type="text" id="pba" placeholder="請輸入用戶名稱" /></label><br /><br />
        <label>生成郵箱:<input type="text" id="oba" readonly /></label>
        <label>生成ID:<input type="text" id="obb" readonly /></label>
    </div>

    <script type="text/javascript">
        //發佈者
        function Publisher(obj){
            this.observers = [];
            var state = obj.value;     //讓該內容不能直接訪問

            //新增兩個對於state的操做 獲取/更新
            this.getState=function(){
                return state;
            }
            this.setState=function(value){
                state = value;
                this.notice();
            }
            this.obj = obj;

        }
        Publisher.prototype.addOb=function(observer){
            var flag = false;
            for (var i = this.observers.length - 1; i >= 0; i--) {
                if(this.observers[i]===observer){
                    flag=true;                
                }
            };
            if(!flag){
                this.observers.push(observer);
            }
            return this;
        }
        Publisher.prototype.removeOb=function(observer){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                if(observers[i]===observer){
                    observers.splice(i,1);
                }
            };
            return this;
        }
        Publisher.prototype.notice=function(){
            var observers = this.observers;
            for (var i = 0; i < observers.length; i++) {
                    observers[i].update(this.getState());
            };
        }

        //訂閱者
        function Subscribe(obj){
            this.obj = obj;
            this.update = function(data){
                this.obj.value = data;
            };
        }

        //實際應用
        var oba = new Subscribe(document.querySelector("#oba")),
            obb = new Subscribe(document.querySelector("#obb"));

        var pba = new Publisher(document.querySelector("#pba"));

        pba.addOb(oba);
        pba.addOb(obb);

        oba.update = function(state){
            this.obj.value = state+"@w3c.com";
        }
        obb.update = function(state){
            this.obj.value = "ID-"+state;
        }

        pba.obj.addEventListener('keyup',function(){
            pba.setState(this.value);
        });
        
    </script>
</body>
</html>

在《大話設計模式》一書中,提到相似的狀況:若是針對發佈者內容而訂閱者要作不一樣的事情呢?好比一個按鈕和三個矩形,點擊按鈕的時候,第一個矩形增長寬度,第二個矩形增長高度,第三個矩形則變成圓角矩形又該怎麼作呢?固然咱們能夠在三個矩形的update內部寫具體的實現代碼,可是這update豈不是沒有一個具體的功能描述了嗎?該書中用「事件委託」解決了這個問題(此處事件委託和DOM中的事件委託應該是兩碼事),我我的理解這個「事件委託」在javascript中能夠用一個數組表示,而後裏面放各個訂閱者的不一樣名字的update,而後一一調用。

在《javascript設計模式》一書中,關於觀察者模式的實現也是採用」推「這種方式,章節的最後反問到如何實現」拉「這種方式呢?

我我的理解:發佈者推送數據的時候有強制性,促使訂閱者更新(update),然而在」拉「這種模式中,發佈者自己僅僅包含最新的內容,沒有通知(notice)沒有訂閱者列表,當訂閱者須要獲得數據的時候在其對應的update方法裏面傳入發佈者對象便可。小白之見,請對該模式有不一樣理解的道友多多指正。o(∩_∩)o 

相關文章
相關標籤/搜索