资讯专栏INFORMATION COLUMN

[js高手之路]设计模式系列课程-发布者,订阅者重构购物车

fevin / 2610人阅读

摘要:发布者订阅者模式,是一种很常见的模式,比如一买卖房子生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色中介拿到卖主的房源信息,根据手头上掌握的客户联系信息买房的人的手机

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

一、买卖房子

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

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

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

二,网站订阅信息的用户

订阅者角色:需要订阅某类信息的网民,如某个网站的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 
 2 
 3 
 4     
 5     Title
 6     
 7 
 8 
 9 
10
    11
  • 12 13 0 14 15 单价: 16 15元; 17 小计: 18 0元 19
  • 20
  • 21 22 0 23 24 单价: 25 10元; 26 小计: 27 0元 28
  • 29
  • 30 31 0 32 33 单价: 34 5元; 35 小计: 36 0元 37
  • 38
  • 39 40 0 41 42 单价: 43 2元; 44 小计: 45 0元 46
  • 47
  • 48 49 0 50 51 单价: 52 1元; 53 小计: 54 0元 55
  • 56
57
58 商品一共 59 0 60 件; 61 一共花费 62 0 63 元; 64 其中最贵的商品单价是0元 65
66
67 68

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 }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85217.html

相关文章

  • [js高手之路]js单页hash路由原理与应用实战

    摘要:什么是路由通俗点说,就是不同的显示不同的内容什么是单页应用单页,英文缩写为,就是把各种功能做在一个页面内那所谓的单页路由应用就是在一个页面内,通过切换地址栏的来实现切换内容的变化如何知道切换了呢当后面的锚文本发生变化时,会触发事件。 什么是路由? 通俗点说,就是不同的URL显示不同的内容 什么是单页应用? 单页,英文缩写为SPA( Single Page Application),就是...

    tinna 评论0 收藏0
  • 深入理解JavaScript系列6:S.O.L.I.D五大原则之单一职责

    摘要:,开始我们的第一篇单一职责。通过解耦可以让每个职责工更加有弹性地变化。关于本文本文转自大叔的深入理解系列。深入理解系列文章,包括了原创,翻译,转载,整理等各类型文章,原文是大叔的一个非常不错的专题,现将其重新整理发布。 前言 Bob大叔提出并发扬了S.O.L.I.D五大原则,用来更好地进行面向对象编程,五大原则分别是: The Single Responsibility Princi...

    walterrwu 评论0 收藏0
  • 实用模式之中介者模式

    摘要:好,师傅我们要学习帝吧人民,进能打,退能刷淘宝。恩,大致过程就是这样,我们使用中介者模式想一想。首先,数据需要放在中介者模式内,用户的一切操作,都会传递给中介者模式,由他来选择是哪一个部分发生改变。 俗话说,一个模式三个坑。 中介者模式应该算最坑的一个模式,坑不在于他的原理。而在于他的名字和其他模式的使用,真尼玛像。首先,中介者 好像是一切模式里面都有的一个东西,比如,享元模式中-元对...

    AlexTuan 评论0 收藏0
  • 设计模式(通往高手之路的必备技能)

    摘要:设计模式的定义在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。从前由于使用的局限性,和做的应用相对简单,不被重视,就更谈不上设计模式的问题。 ‘从大处着眼,从小处着手’,以前对这句话一知半解,自从踏出校门走入社会,开始工作以来,有了越来越深的理解,偶有发现这句话用在程序开发中也有用,所以,近段时间开始尝试着分析jQuery源码,分析angularjs源码,学习设计模式。 设...

    paraller 评论0 收藏0
  • 前端窝 - 收藏集 - 掘金

    摘要:毫无疑问,设计模式于己于他人于系统都是多赢的设计模式使代码编写真正工程化设计模小书前端掘金这是一本关于的小书。 JavaScript 常见设计模式解析 - 掘金设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的;设计...

    李文鹏 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<