[js高手之路]设计模式系列课程-发布者,订阅者重构购物车的实例
发布者订阅者模式,是一种很常见的模式,比如:
一、买卖房子
生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色
中介拿到卖主的房源信息,根据手头上掌握的客户联系信息(买房的人的手机号),通知买房的人,他充当了发布者的角色
卖主想卖掉自己的房子,就需要告诉中介,把信息交给中介发布
二,网站订阅信息的用户
订阅者角色:需要订阅某类信息的网民,如某个网站的javascript类型文章
发布者角色:邮箱服务器,根据网站收集到的用户订阅邮箱,通知用户.
网站主想把信息告诉订阅者,需要把文章相关内容告诉邮箱服务器去发送
等等非常多的例子,不一一列举
本文用网站订阅的方式,推导发布者-订阅者框架,然后用发布者-订阅者框架来重构一个简单的购物车
var site = {}; site.userlist = []; site.subscribe = function( fn ){ this.userlist.push( fn ); } site.publish = function(){ for( var i = 0, len = this.userlist.length; i < len; i++ ){ this.userlist[i].apply( this, arguments ); } } site.subscribe( function( type ){ console.log( "网站发布了" + type + "内容" ); }); site.subscribe( function( type ){ console.log( "网站发布了" + type + "内容" ); }); site.publish( 'javascript' ); site.publish( 'html5' );
site.userlist就是用来保存订阅者
site.subscribe就是具体的订阅者,把每一个订阅者订阅的具体信息保存在site.userlist
site.publish就是发布者:根据保存的userlist,一个个遍历(通知),执行里面的业务逻辑
但是这个,发布订阅者模式,有个问题,不能订阅想要的类型,上例我加了2个订阅者(第11行,第14行),只要网站发了信息,全部能收到,但是有些用户可能只想收到javascript或者html5的,所以,接下来,我们需要继续完善,希望能够接收到具体的信息,不是某人订阅的类型,就不接收
var site = {}; site.userlist = {}; site.subscribe = function (key, fn) { if (!this.userlist[key]) { this.userlist[key] = []; } this.userlist[key].push(fn); } site.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); } } site.subscribe( "javascript", function( title ){ console.log( title ); }); site.subscribe( "es6", function( title ){ console.log( title ); }); site.publish( "javascript", "[js高手之路]寄生组合式继承的优势" ); site.publish( "es6", "[js高手之路]es6系列教程 - var, let, const详解" ); 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新的语义化标签" );
然后,我们来重构一个购物车实例,没有重构之前,我的购物车用的是面向过程:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>title</title> <script src="js/cart.js"></script> </head> <body> <div id="box"> <ul> <li> <input type="button" value="-"> <span class="num">0</span> <input type="button" value="+"> <span>单价:</span> <span class="unit">15元;</span> <span class="label">小计:</span> <span class="subtotal">0</span>元 </li> <li> <input type="button" value="-"> <span class="num">0</span> <input type="button" value="+"> <span>单价:</span> <span class="unit">10元;</span> <span class="label">小计:</span> <span class="subtotal">0</span>元 </li> <li> <input type="button" value="-"> <span class="num">0</span> <input type="button" value="+"> <span>单价:</span> <span class="unit">5元;</span> <span class="label">小计:</span> <span class="subtotal">0</span>元 </li> <li> <input type="button" value="-"> <span class="num">0</span> <input type="button" value="+"> <span>单价:</span> <span class="unit">2元;</span> <span class="label">小计:</span> <span class="subtotal">0</span>元 </li> <li> <input type="button" value="-"> <span class="num">0</span> <input type="button" value="+"> <span>单价:</span> <span class="unit">1元;</span> <span class="label">小计:</span> <span class="subtotal">0</span>元 </li> </ul> <div class="total-box"> 商品一共 <span id="goods-num">0</span> 件; 一共花费 <span id="total-price">0</span> 元; 其中最贵的商品单价是<span id="unit-price">0</span>元 </div> </div> </body> </html>
cart.js文件:
function getbyclass(cname, obj) { var o = null; if (arguments.length == 2) { o = obj; } else { o = document; } var allnode = o.getelementsbytagname("*"); var anode = []; for( var i = 0 ; i < allnode.length; i++ ){ if( allnode[i].classname == cname ){ anode.push( allnode[i] ); } } return anode; } function getsubtotal( unitprice, goodsnum ){ return unitprice * goodsnum; } function getsum(){ //计算总花费 var asubtotal = getbyclass("subtotal"); var res = 0; for( var i = 0; i < asubtotal.length; i++ ){ res += parseint(asubtotal[i].innerhtml); } return res; } function compareunit() { //比单价,找出最高的单价 var anum = getbyclass( "num"); var aunit = getbyclass( "unit"); var temp = 0; for( var i = 0; i < anum.length; i++ ){ if( parseint(anum[i].innerhtml) != 0 ){ if( temp < parseint(aunit[i].innerhtml) ) { temp = parseint(aunit[i].innerhtml); } } } return temp; } window.onload = function () { var ainput = document.getelementsbytagname("input"); var total = 0; var ogoodsnum = document.getelementbyid("goods-num"); var ototalprice = document.getelementbyid("total-price"); var ounitprice = document.getelementbyid("unit-price"); for (var i = 0; i < ainput.length; i++) { if (i % 2 != 0) { //加号 ainput[i].onclick = function () { //当前加号所在行的数量 var anum = getbyclass( "num", this.parentnode ); var n = parseint( anum[0].innerhtml ); n++; anum[0].innerhtml = n; //获取单价 var aunit = getbyclass( "unit", this.parentnode ); var unitprice = parseint(aunit[0].innerhtml); var subtotal = getsubtotal( unitprice, n ); var asubtotal = getbyclass( "subtotal", this.parentnode ); asubtotal[0].innerhtml = subtotal; total++; //商品总数 ogoodsnum.innerhtml = total; ototalprice.innerhtml = getsum(); ounitprice.innerhtml = compareunit(); } }else { ainput[i].onclick = function(){ var anum = getbyclass( "num", this.parentnode ); if ( parseint( anum[0].innerhtml ) != 0 ){ var n = parseint( anum[0].innerhtml ); n--; anum[0].innerhtml = n; //获取单价 var aunit = getbyclass( "unit", this.parentnode ); var unitprice = parseint(aunit[0].innerhtml); var subtotal = getsubtotal( unitprice, n ); var asubtotal = getbyclass( "subtotal", this.parentnode ); asubtotal[0].innerhtml = subtotal; total--; //商品总数 ogoodsnum.innerhtml = total; ototalprice.innerhtml = getsum(); ounitprice.innerhtml = compareunit(); } } } } }
耦合度太高,可维护性很差.
重构之后的购物车:
window.onload = function () { 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) { return false; } for (var i = 0, len = fns.length; i < len; i++) { fns[i].apply(this, arguments); } } }; (function(){ var abtnminus = document.queryselectorall( "#box li>input:first-child"), abtnplus = document.queryselectorall( "#box li>input:nth-of-type(2)"), curnum = 0, curunitprice = 0; for( var i = 0, len = abtnminus.length; i < len; i++ ){ abtnminus[i].index = abtnplus[i].index = i; abtnminus[i].onclick = function(){ (this.parentnode.children[1].innerhtml > 0) && event.publish( "total-goods-num-minus" ); --this.parentnode.children[1].innerhtml < 0 && (this.parentnode.children[1].innerhtml = 0); curunitprice = this.parentnode.children[4].innerhtml; event.publish( "minus-num" + this.index, parseint( curunitprice ), parseint( this.parentnode.children[1].innerhtml ) ); }; abtnplus[i].onclick = function(){ (this.parentnode.children[1].innerhtml >= 0) && event.publish( "total-goods-num-plus" ); this.parentnode.children[1].innerhtml++; curunitprice = this.parentnode.children[4].innerhtml; event.publish( "plus-num" + this.index, parseint( curunitprice ), parseint( this.parentnode.children[1].innerhtml ) ); } } })(); (function(){ var asubtotal = document.queryselectorall("#box .subtotal"), ogoodsnum = document.queryselector("#goods-num"), ototalprice = document.queryselector("#total-price"); event.subscribe( 'total-goods-num-plus', function(){ ++ogoodsnum.innerhtml; }); event.subscribe( 'total-goods-num-minus', function(){ --ogoodsnum.innerhtml; }); for( let i = 0, len = asubtotal.length; i < len; i++ ){ event.subscribe( 'minus-num' + i, function( unitprice, num ){ asubtotal[i].innerhtml = unitprice * num; }); event.subscribe( 'plus-num' + i, function( unitprice, num ){ asubtotal[i].innerhtml = unitprice * num; }); } })(); console.log( event.userlist ); }
以上这篇[js高手之路]设计模式系列课程-发布者,订阅者重构购物车的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。