zoukankan      html  css  js  c++  java
  • [js高手之路]设计模式系列课程-发布者,订阅者重构购物车

    发布者订阅者模式,是一种很常见的模式,比如:

    一、买卖房子

    生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色

    中介拿到卖主的房源信息,根据手头上掌握的客户联系信息(买房的人的手机号),通知买房的人,他充当了发布者的角色

    卖主想卖掉自己的房子,就需要告诉中介,把信息交给中介发布

    二,网站订阅信息的用户

    订阅者角色:需要订阅某类信息的网民,如某个网站的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的,所以,接下来,我们需要继续完善,希望能够接收到具体的信息,不是某人订阅的类型,就不接收

     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的人,才能收到

    我们已经有了一个基本的发布订阅者框架,接下来,把他完善成一个框架,便于其他功能或者其他网站系统的相同功能可以重用他

            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新的语义化标签" );

    然后,我们来重构一个购物车实例,没有重构之前,我的购物车用的是面向过程:

     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文件:

     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 }
  • 相关阅读:
    浅析软件开发项目的前期沟通工作
    .net core 和 WPF 开发升讯威在线客服系统:实现对 IE8 的完全完美支持 【干货】
    产品的定价策略(一):想通过产品挣钱,首先你产品的目标客户得不差钱
    .net core 和 WPF 开发升讯威在线客服系统:使用线程安全的 BlockingCollection 实现高性能的数据处理
    .net core 和 WPF 开发升讯威在线客服系统:使用 TCP协议 实现稳定的客服端
    .net core 和 WPF 开发升讯威在线客服系统:使用 WebSocket 实现访客端通信
    Centos上配置两层nginx转发,把请求转发到外网
    真实字节二面:什么是伪共享?
    关于MVCC,我之前写错了,这次我改好了!
    从家庭主妇到格力老总,董明珠的大女主逆袭之路
  • 原文地址:https://www.cnblogs.com/ghostwu/p/7446183.html
Copyright © 2011-2022 走看看