提高代码的重用性、可读性、可靠性、可维护性。
var singleton = function (name) { this.name=name; this.instance=null; } singleton.prototype.getName=function(){ alert(this.name) } singleton.getInstance=function(name){ if(!this.instance){ this.instance= singleton(name); } return this.instance; } 12345678910111213 惰性单例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>遵循单一职责的单例模式</title> </head> <style> #loginBtn { width: 50px; height: 50px; background: #000; } </style> <body> <div id="loginBtn"> </div> </body> <script> //负责创建登录浮窗对象 var createLoginLayer = function () { var div = document.createElement('div'); div.innerHTML = '我是登录浮窗'; div.style.display = 'none'; document.body.appendChild(div); return div; }; //单例管理,确保这个实例创建并且只会创建一个 var getSingle = function (fn) { var result; return function () { return result || (result = fn.apply(this, arguments)); } }; //需要的时候才初始化实例,并且只初始化一个 document.getElementById('loginBtn').onclick = function () { var loginLayer = getSingle(createLoginLayer)(); console.log(loginLayer) loginLayer.style.display = 'block'; }; </script> </html>
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748在这个例子中,遵循了单一职责原则。一个函数负责一种责任,getSingle负责单例状态的管理,createLoginLayer负责对象实例。
绩效为S 的人年终奖有4 倍工资,绩效为A 的人年终奖有3 倍工资,而绩效为B 的人年终奖是2 倍工资。
var calculateBonus = function (level, salary) { if(level=== "S"){ return salary*4; } if(level === 'A'){ return salary*3; } if(level=== "B"){ return salary*2; } } 1234567891011
这样一个简单的计算年终奖的函数就出现了。但是这样的函数不易于扩展,假设我们现在绩效为S的年终奖变为了3.5倍,等级变多了,就需要改动代码的源码。这样就违反了开闭原则。通过策略模式改版后的计算年终奖的方法如下代码所示:
var strategies = { "S": function (salary) { return salary * 4; }, "A": function (salary) { return salary * 3; }, "B": function (salary) { return salary * 2; } }; var cacularBonus = function (level, salary) { return strategies[level](salary) } var level2=cacularBonus("A",200); 123456789101112131415
我们将计算年终奖的算法,放在一个对象内部,封装起来,在调用的时候可以通过不同的等级获得不同的计算方式。而我们有新的等级或者新的计算方式的时候,我们对该对象进行更改就可以了。避免了在一个函数内部进行计算,提高了可维护性。
//真正的图片 var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function (src) { imgNode.src = src; } } })(); //图片代理 var proxyImage = (function () { var img = new Image; img.onload = function () { myImage.setSrc(this.src); } return { setSrc: function (src) { myImage.setSrc('./loading.svg'); img.src = src; } } })(); proxyImage.setSrc('http://www.cdhrsip.com/static/imgs/high-tech/banner.png?version=201512141756');
123456789101112131415161718192021222324 缓存代理var mult = function () { console.log('开始计算乘积'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; var proxyMult = (function () { var cache = {}; return function () { var args = Array.prototype.join.call(arguments, ','); if (args in cache) { return cache[args]; } return cache[args] = mult.apply(this, arguments); } })(); console.log( proxyMult(1, 2, 3, 4)); // 输出:24 console.log(proxyMult(1, 2, 3, 4)); // 输出:24
1234567891011121314151617181920通过增加缓存代理的方式,mult 函数可以继续专注于自身的职责——计算乘积,缓存的功能
是由代理对象实现
var salesOffices = {};//售楼处 salesOffices.clientList = {};//售楼处的客户列表,缓存列表,存放订阅者的回调函数 salesOffices.listen = function (key, fn) { //如果没有订阅,添加到订阅列表 if (!this.clientList[key]) { this.clientList[key] = []; } this.clientList[key].push(fn);//订阅的消息添加进消息缓存列表 } salesOffices.trigger = function () { // 发布消息 debugger var key = Array.prototype.shift.call(arguments), // 取出订阅消息 fns = this.clientList[key]; // 取出该消息对应的回调函数集合 if (!fns || fns.length === 0) { // 如果没有订阅该消息,则返回 return false; } for (var i = 0, fn; fn = fns[i++];) { console.log(arguments) fn.apply(this, arguments); // (2) // arguments 是发布消息时附送的参数 } }; salesOffices.listen('squareMeter88', function (price) { // 小明订阅88 平方米房子的消息 console.log('价格= ' + price); // 输出: 2000000 }); salesOffices.listen('squareMeter110', function (price) { // 小红订阅110 平方米房子的消息 console.log('价格= ' + price); // 输出: 3000000 }); salesOffices.trigger('squareMeter88', 2000000); // 发布88 平方米房子的价格 salesOffices.trigger('squareMeter110', 3000000); // 发布110 平方米房子的价格
12345678910111213141516171819202122232425262728发布-订阅模式需要有几个元素:
发布者(售楼处)发布者的缓存列表,用户存放用户订阅消息的回调(售楼部的花名册)发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。
命令模式分为三步:
var setCommand=function(btn,fn){ btn.onClick=function(){ fn(); } } 12345 执行命令者:需要接收到接受者的参数,当发送者发出命令时,执行就好
var menu={ refresh:function(){ console.log("刷新") } } 12345 命令对象:不用关心在哪里被调用被谁调用,只需要按需执行就好了
var commondObj=function(rev){ return function(){ rev.refresh(); } } 12345
var closeDoorCommand = { execute: function(){ console.log( '关门' ); } }; var openPcCommand = { execute: function(){ console.log( '开电脑' ); } }; var openQQCommand = { execute: function(){ console.log( '登录QQ' ); } }; var MacroCommand = function(){ return { commandsList: [], add: function( command ){ this.commandsList.push( command ); }, execute: function(){ for ( var i = 0, command; command = this.commandsList[ i++ ]; ){ command.execute(); } } } }; var macroCommand = MacroCommand(); macroCommand.add( closeDoorCommand ); macroCommand.add( openPcCommand ); macroCommand.add( openQQCommand ); macroCommand.execute();
123456789101112131415161718192021222324252627282930313233这是一个组合模式,closeDoorCommand,openPcCommand,openQQCommand是子对象,MacroCommand是组合对象,组合模式的特点:
表示对象的部分-整体层次结构客户希望统一对待树中的所有对象模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常
在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺
序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
var Beverage = function(){};//咖啡和茶抽象为饮料 Beverage.prototype.boilWater = function(){//煮水 console.log( '把水煮沸' ); }; Beverage.prototype.brew = function(){}; // 空方法,应该由子类重写 泡 Beverage.prototype.pourInCup = function(){}; // 空方法,应该由子类重写 倒 Beverage.prototype.addCondiments = function(){}; // 空方法,应该由子类重写 加料 Beverage.prototype.init = function(){ this.boilWater(); this.brew(); this.pourInCup(); this.addCondiments(); }; 12345678910111213
咖啡子类和茶子类继承Beverage并实现它的方法,
var Coffee = function(){}; Coffee.prototype = new Beverage(); Coffee.prototype.brew = function(){ console.log( '用沸水冲泡咖啡' ); }; var Tea=function(){} Tea.prototype=new Beverage(); Tea.prototype.brew = function(){ console.log( '用沸水浸泡茶叶' ); }; 12345678910
在这一个继承重写的步骤中,Beverage.prototype.init这里的代码成为模板方法模式,描述清楚饮料的制作步骤。
亨元模式是一种用于性能优化的模式,如果系统中由于创建了大量的类似的对象而导致内存占用率过高,亨元模式就有用了。
给50个男模特和50个女模特,然后让他们每人分别穿上一件内衣来拍照。var Model = function( sex, underwear){ this.sex = sex; this.underwear= underwear; }; Model.prototype.takePhoto = function(){ console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear); }; for ( var i = 1; i <= 50; i++ ){ var maleModel = new Model( 'male', 'underwear' + i ); maleModel.takePhoto(); }; for ( var j = 1; j <= 50; j++ ){ var femaleModel= new Model( 'female', 'underwear' + j ); femaleModel.takePhoto(); }; 123456789101112131415
这种方式去处理问题,导致在内存中创建了100个差不多的对象。如果是1W个呢?性能上就会达到饱和。
运用亨元模式处理。
剥离外部状态
var Model = function( sex){ this.sex = sex; }; Model.prototype.takePhoto = function(){ console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear); }; var maleModel = new Model( 'male' ), femaleModel = new Model( 'female' ); for ( var i = 1; i <= 50; i++ ){ maleModel.underwear='underwear'+i; maleModel.takePhoto(); }; for ( var j = 1; j <= 50; j++ ){ femaleModel.underwear='underwear'+i; femaleModel.takePhoto(); };
1234567891011121314151617运用了亨元模式之后,在内存中创建了两个对象就完成了所有的事情,故亨元模式的作用场景在:
一个程序中使用了大量的相似对象。由于使用了大量对象,造成很大的内存开销。对象的大多数状态都可以变为外部状态。剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间
的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
在正式购买后,已经支付过500 元定金的用
户会收到100 元的商城优惠券,200 元定金的用户可以收到50 元的优惠券,而之前没有支付定金
的用户只能进入普通购买模式,也就是没有优惠券,且在库存有限的情况下不一定保证能买到。
var order500 = function (orderType, pay, stock) { if (orderType == 1 && pay) { console.log('500 元定金预购, 得到100 优惠券'); } else { order200(orderType, pay, stock); } } var order200 = function (orderType, pay, stock) { if (orderType == 2 && pay) { console.log('200 元定金预购, 得到50 优惠券'); } else { orderNormal(orderType, pay, stock); } } var orderNormal = function (orderType, pay, stock) { if (stock > 0) { console.log('普通购买, 无优惠券'); } else { console.log('库存不足'); } } order500(1, true, 500); // 输出:500 元定金预购, 得到100 优惠券 order500(1, false, 500); // 输出:普通购买, 无优惠券 order500(2, true, 500); // 输出:200 元定金预购, 得到500 优惠券 order500(3, false, 500); // 输出:普通购买, 无优惠券 order500(3, false, 0); // 输出:手机库存不足
1234567891011121314151617181920212223242526这种写法达到了职责链的方式方法,一次向下去查找合适的处理函数,但是加入我们加入添加一个300定金的呢?就需要去改动代码内部,违反了开闭原则,
var order500 = function (orderType, pay, stock) { if (orderType == 1 && pay) { console.log('500 元定金预购, 得到100 优惠券'); } else { return 'nextSuccessor'; } } var order200 = function (orderType, pay, stock) { if (orderType == 2 && pay) { console.log('200 元定金预购, 得到50 优惠券'); } else { return 'nextSuccessor'; } } var orderNormal = function (orderType, pay, stock) { if (stock > 0) { console.log('普通购买, 无优惠券'); } else { console.log('库存不足'); } } var Chain = function (fn) { this.fn = fn; this.successor = null; }; Chain.prototype.setNextSuccessor = function (successor) { return this.successor = successor; }; Chain.prototype.passRequest = function () { var ret = this.fn.apply(this, arguments); if (ret === 'nextSuccessor') { return this.successor && this.successor.passRequest.apply(this.successor, arguments); } return ret; }; var chainOrder500 = new Chain(order500); var chainOrder200 = new Chain(order200); var chainOrderNormal = new Chain(orderNormal); chainOrder500.setNextSuccessor( chainOrder200 ); chainOrder200.setNextSuccessor( chainOrderNormal ); chainOrder500.passRequest( 1, true, 500 ); // 输出:500 元定金预购,得到100 优惠券 chainOrder500.passRequest( 2, true, 500 ); // 输出:200 元定金预购,得到50 优惠券 chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券 chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
1234567891011121314151617181920212223242526272829303132333435363738394041424344中介者模式是迎合迪米特法则的一种实现。迪米特法则也叫最少知识原则,是指一个对象应
该尽可能少地了解另外的对象(类似不和陌生人说话)。如果对象之间的耦合性太高,一个对象
发生改变之后,难免会影响到其他的对象,跟“城门失火,殃及池鱼”的道理是一样的。而在中
介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。
给对象动态地增加职责的方式称为装饰者(decorator)模式。装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。
var plane = { fire: function(){ console.log( '发射普通子弹' ); } } var missileDecorator = function(){ console.log( '发射导弹' ); } var atomDecorator = function(){ console.log( '发射原子弹' ); } var fire1 = plane.fire; plane.fire = function(){ fire1(); missileDecorator(); } var fire2 = plane.fire; plane.fire = function(){ fire2(); atomDecorator(); } plane.fire(); // 分别输出: 发射普通子弹、发射导弹、发射原子弹
1234567891011121314151617181920212223状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。
var OffLightState = function( light ){ this.light = light; }; OffLightState.prototype.buttonWasPressed = function(){ console.log( '弱光' ); // offLightState 对应的行为 this.light.setState( this.light.weakLightState ); // 切换状态到weakLightState }; // WeakLightState: var WeakLightState = function( light ){ this.light = light; }; WeakLightState.prototype.buttonWasPressed = function(){ console.log( '强光' ); // weakLightState 对应的行为 this.light.setState( this.light.strongLightState ); // 切换状态到strongLightState }; // StrongLightState: var StrongLightState = function( light ){ this.light = light; }; StrongLightState.prototype.buttonWasPressed = function(){ console.log( '关灯' ); // strongLightState 对应的行为 this.light.setState( this.light.offLightState ); // 切换状态到offLightState }; var Light = function(){ this.offLightState = new OffLightState( this ); this.weakLightState = new WeakLightState( this ); this.strongLightState = new StrongLightState( this ); this.button = null; }; Light.prototype.init = function(){ var button = document.createElement( 'button' ), self = this; this.button = document.body.appendChild( button ); this.button.innerHTML = '开关'; this.currState = this.offLightState; // 设置当前状态 this.button.onclick = function(){ self.currState.buttonWasPressed(); } }; Light.prototype.setState = function( newState ){ this.currState = newState; };
123456789101112131415161718192021222324252627282930313233343536373839404142拜读《javascript设计模式和开发实践》
设计模式就是对一系列业务或程序运行的封装,设计模式可以写出可维护性高, 运行效率高的代码,但每一种业务场景有不同的开发逻辑,不同的逻辑选用最适合的代码去编写,最终得到的才会是最好的,没有最优的设计模式,只有更好的设计模式,一个好的代码是经过多次的代码重构才能得到,才能算作对设计模式的完整遵循。了解前任给我们总结得出的设计模式,我们可以站在巨人的肩膀上,在自己写代码的时候,重构的时候,考虑代码的实现。是否合理?是否具备可扩展性?强壮性?等。
相关知识
插花六大原则
JavaScript实现的风飓风数据可视化分析
室内花卉六大养护原则
鲜花摆放六大原则
《JavaScript
React Native:用JavaScript开发移动应用
农药如何选用,农药选用六大原则
宴会餐桌花艺设计六大要点
菜根花小宝贝
javascript 返回上一页面:onclick='javascript:history.back(
网址: javascript设计模式与六大原则 https://m.huajiangbk.com/newsview1231254.html
上一篇: 【Python】文件操作 |
下一篇: PHP中.=和+=是什么意思详解 |