首页 > 分享 > javascript设计模式与六大原则

javascript设计模式与六大原则

设计模式的目的

提高代码的重用性、可读性、可靠性、可维护性。

六大原则

单一职责原则 理解:不同的类具备不同的职责,各司其职。做系统设计是,如果发现有一个类拥有了两种职责,那么就要问一个问题:可以将这个类分成两个类吗?如果真的有必要,那就分开,千万不要让一个类干的事情太多。总结:一个类只承担一个职责,一个对象只有一种引起他变化的原因。 里氏替换原则 理解:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。总结:所有引用基类(父类)的地方必须能透明地使用其子类的对象。 依赖倒置原则 理解:举例人吃苹果,我想吃苹果,但是我还想吃橘子,如果按照程序思维的话。就是三个类型,人Class,苹果Class,橘子Class,这种方式冗杂不好维护,不易理解,用水果来抽象化,苹果类继承并实现吃的动作。总结:使用接口或抽象类 接口隔离原则 理解:如果一个类实现一个接口,但这个接口中有它不需要的方法,那么就需要把这个接口拆分,把它需要的方法提取出来,组成一个新的接口让这个类去实现总结:一个接口对实现它的类都是有用的。接口足够小 迪米特原则(最少知道原则) 理解:一个对象应该对其他对象有最少的了解总结:类中只暴露不得不暴露的,其内部实现不暴露出去。 开闭原则 理解:类、模块、函数,可以去扩展,但不要去修改。如果要修改代码,尽量用继承或组合的方式来扩展类的功能总结:是扩展不是修改

(1)单例模式

定义:一个类只有一个实例,并且提供一个访问它的全局访问点。 最简单的单例模式无非是用一个变量来区分当前实例是否创建过,创建了返回之前的,没创建过创建。

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负责对象实例。

(2)策略模式

定义有一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。例子:计算年终奖

绩效为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

我们将计算年终奖的算法,放在一个对象内部,封装起来,在调用的时候可以通过不同的等级获得不同的计算方式。而我们有新的等级或者新的计算方式的时候,我们对该对象进行更改就可以了。避免了在一个函数内部进行计算,提高了可维护性。

(3)代理模式

为一个对象提供一个代用品或占位符,以便控制对它的访问虚拟代理

//真正的图片 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 函数可以继续专注于自身的职责——计算乘积,缓存的功能
是由代理对象实现

(4)迭代器模式

供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示迭代器模式是一种相对简单的模式,简单到很多时候我们都不认为它是一种设计模式。目前
的绝大部分语言都内置了迭代器。

(5)发布-订阅模式(观察者模式)

它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
现实举例::有这个几个人,小红、小强,小明和小龙,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。售楼处是发布者,这几个人是订阅者。

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

发布-订阅模式需要有几个元素:

发布者(售楼处)发布者的缓存列表,用户存放用户订阅消息的回调(售楼部的花名册)发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)

(6)命令模式

有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。
命令模式分为三步:

发送者: 不关心给哪个button,以及绑定什么事件,只要通过参数传入就好

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

(7)组合模式

用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成

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是组合对象,组合模式的特点:

表示对象的部分-整体层次结构客户希望统一对待树中的所有对象

(8)模板方法模式

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常
在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺
序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

coffice or tea
泡咖啡步骤:1.把水煮沸2.用沸水冲泡咖啡3.把咖啡倒进杯子4.加糖和牛奶
泡茶步骤:1.把水煮沸2.用沸水浸泡茶叶3.把茶水倒进杯子4.加柠檬
抽象对象Beverage:1.把水煮沸2.用沸水泡3.把饮料倒进杯子4.加料

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这里的代码成为模板方法模式,描述清楚饮料的制作步骤。

(9)亨元模式

亨元模式是一种用于性能优化的模式,如果系统中由于创建了大量的类似的对象而导致内存占用率过高,亨元模式就有用了。

给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

运用了亨元模式之后,在内存中创建了两个对象就完成了所有的事情,故亨元模式的作用场景在:

一个程序中使用了大量的相似对象。由于使用了大量对象,造成很大的内存开销。对象的大多数状态都可以变为外部状态。剥离出对象的外部状态之后,可以用相对较少的共享对象取代大量对象。

(10)职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间
的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

售卖手机的电商网站

在正式购买后,已经支付过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

(11)中介者模式

中介者模式是迎合迪米特法则的一种实现。迪米特法则也叫最少知识原则,是指一个对象应
该尽可能少地了解另外的对象(类似不和陌生人说话)。如果对象之间的耦合性太高,一个对象
发生改变之后,难免会影响到其他的对象,跟“城门失火,殃及池鱼”的道理是一样的。而在中
介者模式里,对象之间几乎不知道彼此的存在,它们只能通过中介者对象来互相影响对方。

(12)装饰者模式

给对象动态地增加职责的方式称为装饰者(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

(13)状态模式

状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。

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中.=和+=是什么意思详解