[js高手之路]設計模式系列課程-發佈者,訂閱者重構購物車

發佈者訂閱者模式,是一種很常見的模式,好比:
1、買賣房子
生活中的買房,賣房,中介就構成了一個發佈訂閱者模式,買房的人,通常須要的是房源,價格,使用面積等信息,他充當了訂閱者的角色
中介拿到賣主的房源信息,根據手頭上掌握的客戶聯繫信息(買房的人的手機號),通知買房的人,他充當了發佈者的角色
賣主想賣掉本身的房子,就須要告訴中介,把信息交給中介發佈
二,網站訂閱信息的用戶
訂閱者角色:須要訂閱某類信息的網民,如某個網站的javascript類型文章
發佈者角色:郵箱服務器,根據網站收集到的用戶訂閱郵箱,通知用戶.
網站主想把信息告訴訂閱者,須要把文章相關內容告訴郵箱服務器去發送
等等很是多的例子,不一一列舉
本文用網站訂閱的方式,推導發佈者-訂閱者框架,而後用發佈者-訂閱者框架來重構一個簡單的購物車javascript

1         var Site = {};
 2         Site.userList = [];
 3         Site.subscribe = function( fn ){
 4             this.userList.push( fn );
 5         }
 6         Site.publish = function(){
 7            for( var i = 0, len = this.userList.length; i < len; i++ ){
 8                 this.userList[i].apply( this, arguments );
 9            } 
10         }
11         Site.subscribe( function( type ){
12             console.log( "網站發佈了" + type + "內容" );
13         });
14         Site.subscribe( function( type ){
15             console.log( "網站發佈了" + type + "內容" );
16         });
17         Site.publish( 'javascript' );
18         Site.publish( 'html5' );

Site.userList就是用來保存訂閱者
Site.subscribe就是具體的訂閱者,把每個訂閱者訂閱的具體信息保存在Site.userList
Site.publish就是發佈者:根據保存的userList,一個個遍歷(通知),執行裏面的業務邏輯
可是這個,發佈訂閱者模式,有個問題,不能訂閱想要的類型,上例我加了2個訂閱者(第11行,第14行),只要網站發了信息,所有能收到,可是有些用戶可能只想收到javascript或者html5的,因此,接下來,咱們須要繼續完善,但願可以接收到具體的信息,不是某人訂閱的類型,就不接收html

1         var Site = {};
 2         Site.userList = {};
 3         Site.subscribe = function (key, fn) {
 4             if (!this.userList[key]) {
 5                 this.userList[key] = [];
 6             }
 7             this.userList[key].push(fn);
 8         }
 9         Site.publish = function () {
10             var key = Array.prototype.shift.apply(arguments),
11                 fns = this.userList[key];
12             if ( !fns || fns.length === 0) {
13                 console.log( '沒有人訂閱' + key + "這個分類的文章" );
14                 return false;
15             }
16             for (var i = 0, len = fns.length; i < len; i++) {
17                 fns[i].apply(this, arguments);
18             }
19         }
20 
21         Site.subscribe( "javascript", function( title ){
22             console.log( title );
23         });
24 
25         Site.subscribe( "es6", function( title ){
26             console.log( title );
27         });
28 
29         Site.publish( "javascript", "[js高手之路]寄生組合式繼承的優點" );
30         Site.publish( "es6", "[js高手之路]es6系列教程 - var, let, const詳解" );
31         Site.publish( "html5", "html5新的語義化標籤" );

輸出結果:
[js高手之路]寄生組合式繼承的優點
[js高手之路]es6系列教程 - var, let, const詳解
沒有人訂閱html5這個分類的文章
咱們能夠看到,只有訂閱了javascript類型文章的人,才能收到 」寄生組合式繼承的優點」 這篇文章,發佈html5類型的時候,沒有任何人會收到.
es6類型的,只有訂閱es6的人,才能收到
咱們已經有了一個基本的發佈訂閱者框架,接下來,把他完善成一個框架,便於其餘功能或者其餘網站系統的相同功能能夠重用他html5

var Event = {
            userList : {},
            subscribe : function (key, fn) {
                if (!this.userList[key]) {
                    this.userList[key] = [];
                }
                this.userList[key].push(fn);
            },
            publish : function () {
                var key = Array.prototype.shift.apply(arguments),
                    fns = this.userList[key];
                if (!fns || fns.length === 0) {
                    console.log('沒有人訂閱' + key + "這個分類的文章");
                    return false;
                }
                for (var i = 0, len = fns.length; i < len; i++) {
                    fns[i].apply(this, arguments);
                }
            }
        };

        var extend = function( dstObj, srcObj ){
            for( var key in srcObj ){
                dstObj[key] = srcObj[key];
            }
        }

        var Site = {};
        extend( Site, Event );
         Site.subscribe( "javascript", function( title ){
            console.log( title );
        });

        Site.subscribe( "es6", function( title ){
            console.log( title );
        });

        Site.publish( "javascript", "寄生組合式繼承的優點" );
        Site.publish( "es6", "es6系列教程 - var, let, const詳解" );
        Site.publish( "html5", "html5新的語義化標籤" );

而後,咱們來重構一個購物車實例,沒有重構以前,個人購物車用的是面向過程:java

1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <script src="js/cart.js"></script>
 7 </head>
 8 <body>
 9 <div id="box">
10     <ul>
11         <li>
12             <input type="button" value="-">
13             <span class="num">0</span>
14             <input type="button" value="+">
15             <span>單價:</span>
16             <span class="unit">15元;</span>
17             <span class="label">小計:</span>
18             <span class="subtotal">0</span>元
19         </li>
20         <li>
21             <input type="button" value="-">
22             <span class="num">0</span>
23             <input type="button" value="+">
24             <span>單價:</span>
25             <span class="unit">10元;</span>
26             <span class="label">小計:</span>
27             <span class="subtotal">0</span>元
28         </li>
29         <li>
30             <input type="button" value="-">
31             <span class="num">0</span>
32             <input type="button" value="+">
33             <span>單價:</span>
34             <span class="unit">5元;</span>
35             <span class="label">小計:</span>
36             <span class="subtotal">0</span>元
37         </li>
38         <li>
39             <input type="button" value="-">
40             <span class="num">0</span>
41             <input type="button" value="+">
42             <span>單價:</span>
43             <span class="unit">2元;</span>
44             <span class="label">小計:</span>
45             <span class="subtotal">0</span>元
46         </li>
47         <li>
48             <input type="button" value="-">
49             <span class="num">0</span>
50             <input type="button" value="+">
51             <span>單價:</span>
52             <span class="unit">1元;</span>
53             <span class="label">小計:</span>
54             <span class="subtotal">0</span>元
55         </li>
56     </ul>
57     <div class="total-box">
58         商品一共
59         <span id="goods-num">0</span>
60         件;
61         一共花費
62         <span id="total-price">0</span>
63         元;
64         其中最貴的商品單價是<span id="unit-price">0</span>元
65     </div>
66 </div>
67 </body>
68 </html>

cart.js文件:es6

1 function getByClass(cName, obj) {
 2     var o = null;
 3     if (arguments.length == 2) {
 4         o = obj;
 5     } else {
 6         o = document;
 7     }
 8     var allNode = o.getElementsByTagName("*");
 9     var aNode = [];
10     for( var i = 0 ; i < allNode.length; i++ ){
11        if( allNode[i].className == cName ){
12           aNode.push( allNode[i] );
13        }
14     }
15     return aNode;
16 }
17 
18 function getSubTotal( unitPrice, goodsNum ){
19     return unitPrice * goodsNum;
20 }
21 
22 function getSum(){ //計算總花費
23     var aSubtotal = getByClass("subtotal");
24     var res = 0;
25     for( var i = 0; i < aSubtotal.length; i++ ){
26        res += parseInt(aSubtotal[i].innerHTML);
27     }
28     return res;
29 }
30 
31 function compareUnit() { //比單價,找出最高的單價
32     var aNum = getByClass( "num");
33     var aUnit = getByClass( "unit");
34     var temp = 0;
35     for( var i = 0; i < aNum.length; i++ ){
36        if( parseInt(aNum[i].innerHTML) != 0 ){
37             if( temp < parseInt(aUnit[i].innerHTML) ) {
38                temp = parseInt(aUnit[i].innerHTML);
39             }
40        }
41     }
42     return temp;
43 }
44 
45 window.onload = function () {
46     var aInput = document.getElementsByTagName("input");
47     var total = 0;
48     var oGoodsNum = document.getElementById("goods-num");
49     var oTotalPrice = document.getElementById("total-price");
50     var oUnitPrice = document.getElementById("unit-price");
51 
52     for (var i = 0; i < aInput.length; i++) {
53         if (i % 2 != 0) { //加號
54             aInput[i].onclick = function () {
55                 //當前加號所在行的數量
56                 var aNum = getByClass( "num", this.parentNode );
57                 var n = parseInt( aNum[0].innerHTML );
58                 n++;
59                 aNum[0].innerHTML = n;
60                 //獲取單價
61                 var aUnit = getByClass( "unit", this.parentNode );
62                 var unitPrice = parseInt(aUnit[0].innerHTML);
63                 var subtotal = getSubTotal( unitPrice, n );
64                 var aSubtotal = getByClass( "subtotal", this.parentNode );
65                 aSubtotal[0].innerHTML = subtotal;
66                 total++; //商品總數
67                 oGoodsNum.innerHTML = total;
68                 oTotalPrice.innerHTML = getSum();
69                 oUnitPrice.innerHTML = compareUnit();
70             }
71         }else {
72             aInput[i].onclick = function(){
73                 var aNum = getByClass( "num", this.parentNode );
74                 if ( parseInt( aNum[0].innerHTML ) != 0 ){
75                     var n = parseInt( aNum[0].innerHTML );
76                     n--;
77                     aNum[0].innerHTML = n;
78                     //獲取單價
79                     var aUnit = getByClass( "unit", this.parentNode );
80                     var unitPrice = parseInt(aUnit[0].innerHTML);
81                     var subtotal = getSubTotal( unitPrice, n );
82                     var aSubtotal = getByClass( "subtotal", this.parentNode );
83                     aSubtotal[0].innerHTML = subtotal;
84                     total--; //商品總數
85                     oGoodsNum.innerHTML = total;
86                     oTotalPrice.innerHTML = getSum();
87                     oUnitPrice.innerHTML = compareUnit();
88                 }
89             }
90         }
91     }
92 }

耦合度過高,可維護性不好.
重構以後的購物車:設計模式

1 window.onload = function () {
 2     var Event = {
 3         userList: {},
 4         subscribe: function (key, fn) {
 5             if (!this.userList[key]) {
 6                 this.userList[key] = [];
 7             }
 8             this.userList[key].push(fn);
 9         },
10         publish: function () {
11             var key = Array.prototype.shift.apply(arguments),
12                 fns = this.userList[key];
13             if (!fns || fns.length === 0) {
14                 return false;
15             }
16             for (var i = 0, len = fns.length; i < len; i++) {
17                 fns[i].apply(this, arguments);
18             }
19         }
20     };
21     (function(){
22         var aBtnMinus = document.querySelectorAll( "#box li>input:first-child"),
23             aBtnPlus = document.querySelectorAll( "#box li>input:nth-of-type(2)"),
24             curNum = 0, curUnitPrice = 0;
25 
26         for( var i = 0, len = aBtnMinus.length; i < len; i++ ){
27             aBtnMinus[i].index = aBtnPlus[i].index = i;
28             aBtnMinus[i].onclick = function(){
29                 (this.parentNode.children[1].innerHTML > 0) && Event.publish( "total-goods-num-minus" );
30                 --this.parentNode.children[1].innerHTML < 0 && (this.parentNode.children[1].innerHTML = 0);
31                 curUnitPrice = this.parentNode.children[4].innerHTML;
32                 Event.publish( "minus-num" + this.index, 
33                     parseInt( curUnitPrice ),
34                     parseInt( this.parentNode.children[1].innerHTML )
35                 );
36             };
37             aBtnPlus[i].onclick = function(){
38                 (this.parentNode.children[1].innerHTML >= 0) && Event.publish( "total-goods-num-plus" );
39                 this.parentNode.children[1].innerHTML++;
40                 curUnitPrice = this.parentNode.children[4].innerHTML;
41                 Event.publish( "plus-num" + this.index, 
42                     parseInt( curUnitPrice ),
43                     parseInt( this.parentNode.children[1].innerHTML )
44                 );
45             }
46         }
47     })();
48     (function(){
49         var aSubtotal = document.querySelectorAll("#box .subtotal"),
50             oGoodsNum = document.querySelector("#goods-num"),
51             oTotalPrice = document.querySelector("#total-price");
52             Event.subscribe( 'total-goods-num-plus', function(){
53                 ++oGoodsNum.innerHTML;
54             });
55             Event.subscribe( 'total-goods-num-minus', function(){
56                 --oGoodsNum.innerHTML;
57             });
58         for( let i = 0, len = aSubtotal.length; i < len; i++ ){
59             Event.subscribe( 'minus-num' + i, function( unitPrice, num ){
60                 aSubtotal[i].innerHTML = unitPrice * num;
61             });
62             Event.subscribe( 'plus-num' + i, function( unitPrice, num ){
63                 aSubtotal[i].innerHTML = unitPrice * num;
64             });
65         }
66     })();
67     console.log( Event.userList );
68 }

轉載於猿2048:☞[《[js高手之路]設計模式系列課程-發佈者,訂閱者重構購物車》][1]服務器

相關文章
相關標籤/搜索