[js高手之路]设计模式系列课程-发布者,订阅者重构购物车
来源:互联网 发布:岳阳蔬菜网络销售 编辑:程序博客网 时间:2024/05/22 17:09
发布者订阅者模式,是一种很常见的模式,比如:
一、买卖房子
生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色
中介拿到卖主的房源信息,根据手头上掌握的客户联系信息(买房的人的手机号),通知买房的人,他充当了发布者的角色
卖主想卖掉自己的房子,就需要告诉中介,把信息交给中介发布
二,网站订阅信息的用户
订阅者角色:需要订阅某类信息的网民,如某个网站的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 >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 >0</span>60 件;61 一共花费62 <span >0</span>63 元;64 其中最贵的商品单价是<span >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 }
- [js高手之路]设计模式系列课程-发布者,订阅者重构购物车
- [js高手之路]设计模式系列课程-单例模式实现模态框
- [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表
- [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表
- [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表
- [js高手之路]设计模式系列课程-设计一个模块化扩展功能(define)和使用(use)库
- [js高手之路]设计模式系列课程-设计一个模块化扩展功能(define)和使用(use)库
- [js高手之路]设计模式系列课程-设计一个模块化扩展功能(define)和使用(use)库
- 发布者订阅者模式
- 一个订阅发布者模式
- 订阅/发布者模式---JavaScript
- js设计模式之订阅
- activemq发布者/订阅模式模式
- JS设计模式之发布订阅模式
- ZeroMQ之模式二 发布者订阅者(publish-subscribe)
- [js高手之路]node js系列课程-创建简易web服务器与文件读写
- [js高手之路]node js系列课程-创建简易web服务器与文件读写
- 发布者订阅者模式(一)
- RadioButton选中
- Linux压缩、解压文件(随记)
- java 导出excel(POI)
- 中国航天科工开展了 “高速飞行列车”的研究论证
- Nodejs进阶:使用DiffieHellman密钥交换算法
- [js高手之路]设计模式系列课程-发布者,订阅者重构购物车
- Tomcat Manager用户配置
- 一致性哈希和取模哈希的问题
- CURL error 60:
- 【C#】简单的消息管理机制
- linux在局域网下设置时间同步
- 简单的点击链接隐藏与显示div
- ajax 跨域访问java 带请求头
- 去掉标题栏,全屏