[js高手之路]设计模式系列课程-发布者,订阅者重…
2018-06-24 00:04:05来源:未知 阅读 ()
发布者订阅者模式,是一种很常见的模式,比如:
一、买卖房子
生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色
中介拿到卖主的房源信息,根据手头上掌握的客户联系信息(买房的人的手机号),通知买房的人,他充当了发布者的角色
卖主想卖掉自己的房子,就需要告诉中介,把信息交给中介发布
二,网站订阅信息的用户
订阅者角色:需要订阅某类信息的网民,如某个网站的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 }
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- javascript设计模式 接口介绍 2019-09-23
- [JS设计模式]:观察者模式(即发布-订阅者模式)(4) 2019-08-14
- Vue学习之路由vue-router传参及嵌套小结(十) 2019-08-14
- Vue学习之路由vue-router小结(九) 2019-08-14
- html.css.javascript 跟随着我一起走向前端之路 2019-05-22
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash