资讯专栏INFORMATION COLUMN

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

fevin / 2446人阅读

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

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

一、买卖房子

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

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

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

二,网站订阅信息的用户

订阅者角色:需要订阅某类信息的网民,如某个网站的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设计模式与开发实践 - 观察者模式

    摘要:发布者的状态发生变化时就会通知所有的订阅者,使得它们能够自动更新自己。观察者模式的中心思想就是促进松散耦合,一为时间上的解耦,二为对象之间的解耦。参考设计模式与开发实践第章发布订阅模式设计模式第章第节观察者模式 概述 观察者模式又叫发布 - 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫...

    xiangzhihong 评论0 收藏0

发表评论

0条评论

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