设计模式及js实现(三)行为型模式 23种设计模式根据其目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三种:
创建型模式 主要用于创建对象。
结构型模式 主要用于处理类或对象的组合。
行为型模式 主要用于描述对类或对象怎样交互和怎样分配职责。
目录 行为型模式 - 职责链模式(Chain of Responsibility) - 命令模式(Command) - 解释器模式(Interpreter) - 迭代器模式(Iterator) - 中介者模式(Mediator) - 备忘录模式(Memento) - 观察者模式(Observer) - 状态模式(State) - 策略模式(Strategy) - 模板方法模式(Template Method) - 访问者模式(Visitor)
行为型模式 职责链模式(Chain of Responsibility)
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。责任链模式:多个对象均有机会处理请求,从而解除发送者和接受者之间的耦合关系。这些对象连接成为链式结构,每个节点转发请求,直到有对象处理请求为止。
优缺点
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var MoneyStack = function (billSize ) { this .billSize = billSize; this .next = null ; }; MoneyStack .prototype = { withdraw : function (amount ) { var numOfBills = Math .floor (amount / this .billSize ); if (numOfBills > 0 ) { this ._ejectMoney (numOfBill); amount = amount - (this .billSize * numOfBills); } amount > 0 && this .next && this .next .withdraw (amount); }, setNextStack : function (stack ) { this .next = stack; }, _ejectMoney : function (numOfBills ) { console .log (numOfBills + " $" + this .billSize + " bill(s) has/have been spit out" ); } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 var ATM = function ( ) { var stack100 = new MoneyStack (100 ), stack50 = new MoneyStack (50 ), stack20 = new MoneyStack (20 ), stack10 = new MoneyStack (10 ), stack5 = new MoneyStack (5 ), stack1 = new MoneyStack (1 ); stack100.setNextStack (stack50); stack50.setNextStack (stack20); stack20.setNextStack (stack10); stack10.setNextStack (stack5); stack5.setNextStack (stack1); this .moneyStacks = stack100; } ATM .prototype .withdraw = function (amount ) { this .moneyStacks .withdraw (amount); } var atm = new ATM ();atm.withdraw (186 ); atm.withdraw (72 );
ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 class Handler { constructor ( ) { this .next = null ; } setNext (handler ) { this .next = handler; } } class LogHandler extends Handler { constructor (...props ) { super (...props); this .name = "log" ; } handle (level, msg ) { if (level === this .name ) { console .log (`LOG: ${msg} ` ); return ; } this .next && this .next .handle (...arguments ); } } class WarnHandler extends Handler { constructor (...props ) { super (...props); this .name = "warn" ; } handle (level, msg ) { if (level === this .name ) { console .log (`WARN: ${msg} ` ); return ; } this .next && this .next .handle (...arguments ); } } class ErrorHandler extends Handler { constructor (...props ) { super (...props); this .name = "error" ; } handle (level, msg ) { if (level === this .name ) { console .log (`ERROR: ${msg} ` ); return ; } this .next && this .next .handle (...arguments ); } } let logHandler = new LogHandler ();let warnHandler = new WarnHandler ();let errorHandler = new ErrorHandler ();logHandler.setNext (warnHandler); warnHandler.setNext (errorHandler); logHandler.handle ("error" , "Some error occur" );
命令模式(Command) 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。命令模式是一种数据驱动的设计模式,它属于行为型模式。
命令模式的执行步骤:
1.请求以命令的形式包裹在对象中,并传给调用对象。
2.调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象。
3.该对象执行命令。
在这三步骤中,分别有 3 个不同的主体:发送者、传递者和执行者。在实现过程中,需要特别关注。
优缺点
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <button id ="replay" > 播放录像</button > <script > let Ryu = { attack : function ( ){ console .log ( '攻击' ) }, defense : function ( ){ console .log ( '防御' ) }, jump : function ( ){ console .log ( '跳跃' ) }, crouch : function ( ){ console .log ( '蹲下' ) } } let makeCommand = function ( receiver, state ){ return function ( ){ receiver[ state ]() } } let commands = { "119" : "jump" , "115" : "crouch" , "97" : "defense" , "100" : "attack" } let commandStack = [] document .onkeypress = function ( ev ){ let keyCode = ev.keyCode , command = makeCommand ( Ryu , commands[ keyCode ] ) if ( command ){ command () commandStack.push ( command ) } } document .getElementById ( 'replay' ).onclick = function ( ){ let command while ( command = commandStack.shift () ){ command () } } </script > </body > </html >
ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 const MenuBar = { refresh ( ) { console .log ("刷新菜单页面" ); } }; const RefreshMenuBarCommand = receiver => { return { execute ( ) { receiver.refresh (); } }; }; const setCommand = (button, command ) => { button.onclick = () => { command.execute (); }; }; let refreshMenuBarCommand = RefreshMenuBarCommand (MenuBar );let button = document .querySelector ("button" );setCommand (button, refreshMenuBarCommand);
解释器模式(Interpreter) 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。解释器模式: 提供了评估语言的语法或表达式的方式。
这是基本不怎么使用的一种设计模式。 确实想不到什么场景一定要用此种设计模式。
实现这种模式的核心是:
抽象表达式:主要有一个interpret()操作
终结符表达式:R = R1 + R2中,R1 R2就是终结符
非终结符表达式:R = R1 - R2中,-就是终结符
环境(Context): 存放文法中各个终结符所对应的具体值。比如前面R1和R2的值。
优缺点
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护 。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
js实现 ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Context { constructor ( ) { this ._list = []; this ._sum = 0 ; } get sum () { return this ._sum ; } set sum (newValue ) { this ._sum = newValue; } add (expression ) { this ._list .push (expression); } get list () { return [...this ._list ]; } } class PlusExpression { interpret (context ) { if (!(context instanceof Context )) { throw new Error ("TypeError" ); } context.sum = ++context.sum ; } } class MinusExpression { interpret (context ) { if (!(context instanceof Context )) { throw new Error ("TypeError" ); } context.sum = --context.sum ; } } const context = new Context ();context.add (new PlusExpression ()); context.add (new PlusExpression ()); context.add (new MinusExpression ()); context.list .forEach (expression => expression.interpret (context)); console .log (context.sum );
迭代器模式(Iterator) 迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式是指提供一种方法顺序访问一个集合对象的各个元素,使用者不需要了解集合对象的底层实现。
优缺点
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 var getActiveUploadObj = function ( ) { try { return new ActiveXObject ("TXFTNActiveX.FTNUpload" ); } catch (e) { return false ; } }; var getFlashUploadObj = function ( ) { if (supportFlash ()) { var str = '<object type="application/x-shockwave-flash"></object>' ; return $(str).appendTo ($('body' )); }; return false ; } var getFormUpladObj = function ( ) { var str = '<input name="file" type="file" class="ui-file"/>' ; return $(str).appendTo ($('body' )); }; var iteratorUpload = function ( ) { for (var i = 0 , fn; fn = arguments [i++];) { var upload = fn (); if (upload !== false ) { return upload; } } }; var uploadObj = iteratorUpload ( getActiveUploadObj, getFlashUploadObj, getFormUpladObj );var getWebkitUploadObj = function ( ){ } var getHtml5UploadObj = function ( ){ } var uploadObj = iteratorUploadObj ( getActiveUploadObj, getWebkitUploadObj,getFlashUploadObj, getHtml5UploadObj, getFormUpladObj );
ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Iterator = obj => { let current = 0 ; let next = ( ) => (current += 1 ); let end = ( ) => current >= obj.length ; let get = ( ) => obj[current]; return { next, end, get }; }; let myIter = Iterator ([1 , 2 , 3 ]);while (!myIter.end ()) { console .log (myIter.get ()); myIter.next (); }
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
优缺点
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会庞大,变得复杂难以维护。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 var Participant = function (name ) { this .name = name; this .chatroom = null ; }; Participant .prototype = { send : function (message, to ) { this .chatroom .send (message, this , to); }, receive : function (message, from ) { log.add (from .name + " to " + this .name + ": " + message); } }; var Chatroom = function ( ) { var participants = {}; return { register : function (participant ) { participants[participant.name ] = participant; participant.chatroom = this ; }, send : function (message, from , to ) { if (to) { to.receive (message, from ); } else { for (key in participants) { if (participants[key] !== from ) { participants[key].receive (message, from ); } } } } }; }; var log = (function ( ) { var log = "" ; return { add : function (msg ) { log += msg + "\n" ; }, show : function ( ) { alert (log); log = "" ; } } })(); function run ( ) { var yoko = new Participant ("Yoko" ); var john = new Participant ("John" ); var paul = new Participant ("Paul" ); var ringo = new Participant ("Ringo" ); var chatroom = new Chatroom (); chatroom.register (yoko); chatroom.register (john); chatroom.register (paul); chatroom.register (ringo); yoko.send ("All you need is love." ); yoko.send ("I love you John." ); john.send ("Hey, no need to broadcast" , yoko); paul.send ("Ha, I heard that!" ); ringo.send ("Paul, what do you think?" , paul); log.show (); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 <!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 > <body > 选择颜色: <select id ="colorSelect" > <option value ="" > 请选择</option > <option value ="red" > 红色</option > <option value ="blue" > 蓝色</option > </select > 选择内存: <select id ="memorySelect" > <option value ="" > 请选择</option > <option value ="32G" > 32G</option > <option value ="16G" > 16G</option > </select > 输入购买数量: <input type ="text" id ="numberInput" > 您选择了颜色:<div id ="colorInfo" > </div > <br > 您选择了内存:<div id ="memoryInfo" > </div > <br > 您输入了数量:<div id ="numberInfo" > </div > <br > <button id ="nextBtn" disabled > 请选择手机颜色、内存和购买数量</button > </body > <script > var goods = { 'red|32G' : 3 , 'red|16G' : 0 , 'blue|32G' : 1 , 'blue|16G' : 6 } var mediator = (function ( ){ var colorSelect = document .getElementById ('colorSelect' ), memorySelect = document .getElementById ('memorySelect' ), numberInput = document .getElementById ('numberInput' ), colorInfo = document .getElementById ('colorInfo' ), memoryInfo = document .getElementById ('memoryInfo' ), numberInfo = document .getElementById ('numberInfo' ), nextBtn = document .getElementById ('nextBtn' ); return { changed : function (obj ){ var color = colorSelect.value , memory = memorySelect.value , number = numberInput.value , stock = goods[color + '|' + memory]; if (obj == colorSelect){ colorInfo.innerHTML = color; }else if (obj == memorySelect){ memoryInfo.innerHTML = memory; }else if (obj == numberInput){ numberInfo.innerHTML = number; } if (!color){ nextBtn.disabled = true ; nextBtn.innerHTML = '请选择手机颜色' ; return ; } if (!memory){ nextBtn.disabled = true ; nextBtn.innerHTML = '请选择手机内存' ; return ; } if (!number){ nextBtn.disabled = true ; nextBtn.innerHTML = '请填写手机数量' ; return ; } if ( ( (number-0 ) | 0 ) !== number-0 ){ nextBtn.disabled = true ; nextBtn.innerHTML = '请输入正确的购买数量' ; return ; } if (number > stock){ nextBtn.disabled = true ; nextBtn.innerHTML = '库存不足' ; return ; } nextBtn.disabled = false ; nextBtn.innerHTML = '放入购物车' ; } } })() colorSelect.onchange = function ( ){ mediator.changed (this ) } memorySelect.onchange = function ( ){ mediator.changed (this ) } numberInput.oninput = function ( ){ mediator.changed (this ) } </script > </html >
备忘录模式(Memento) 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。它属于行为模式,保存某个状态,并且在需要的时候直接获取,而不是重复计算。
备忘录模式实现,不能破坏原始封装。 也就是说,能拿到内部状态,将其保存在外部。
优缺点
优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
js实现 原:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $('#next_page_btn' ).click (function ( ) { var $news = $('#news_content' ); var page = $news.data ('page' ); getPageDataFromServer (page, function ( ) { $news.data ('page' , page+1 ); }) }); $('#pre_page_btn' ).click (function ( ) { var $news = $('#news_content' ); var page = $news.data ('page' ); getPageDataFromServer (page, function ( ) { $news.data ('page' , page-1 ); }); });
改为备忘录模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var Page = function ( ) { var cache = {}; return function (page, fn ) { if (cache[page]) { showPage (page, cache[page]); fn && fn (); } else { $.post ('server_api_url' , {page : page}, function (res ) { if (res.success ) { showPage (page, res.data ); cache[page] = res.data ; fn && fn (); } else { } }); } } }(); $('#next_page_btn' ).click (function ( ) { var $news = $('#news_content' ); var page = $news.data ('page' ); Page (page, function ( ) { $news.data ('page' , page+1 ); }); }); $('#pre_page_btn' ).click (function ( ) { var $news = $('#news_content' ); var page = $news.data ('page' ); Page (page, function ( ) { $news.data ('page' , page-1 ); }); });
观察者模式(Observer) 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
优缺点
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 const event = { clientList : [], listen : function (key , fn ) { if (this .clientListen [key]) { this .clientList [key] = [] } this .clientList [key].push (fn) }, trigger : function ( ) { const key = Array .prototype .shift .call (arguments ) const fns = this .clientList [key] if (!fns || fns.length === 0 ) { return false } for (let i = 0 , fn ;fn = fns[i++];) { fn.apply (this , arguments ) } }, remove : function (key , fn ) { const fns = this .clientList [key] if (!fns) { return false } if (!fn) { fns && (fns.length = 0 ) } else { for (let l = fns.length - 1 ; l>=0 ; l--) { const _fn = fns[l] if ( _fn ===fn) { fns.splice (l, 1 ) } } } } const installEvent = (obj ) => { for (let i in event) { obj[i] = event[i] } }; const events = {}installEvent (events)events.listen ('newMessage' ,fn1 = (say ) => { console .log ('say:' + say) }) events.trigger ('newMessage' ,"Hello world" ) events.remove ('newMessage' ,fn1)
ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class PubSub { constructor ( ) { this .handles = {}; } on (eventType, handle ) { if (!this .handles .hasOwnProperty (eventType)) { this .handles [eventType] = []; } if (typeof handle == 'function' ) { this .handles [eventType].push (handle); } else { throw new Error ('缺少回调函数' ); } return this ; } emit (eventType, ...args ) { if (this .handles .hasOwnProperty (eventType)) { this .handles [eventType].forEach ((item, key, arr ) => { item.apply (null , args); }) } else { throw new Error (`"${eventType} "事件未注册` ); } return this ; } off (eventType, handle ) { if (!this .handles .hasOwnProperty (eventType)) { throw new Error (`"${eventType} "事件未注册` ); } else if (typeof handle != 'function' ) { throw new Error ('缺少回调函数' ); } else { this .handles [eventType].forEach ((item, key, arr ) => { if (item == handle) { arr.splice (key, 1 ); } }) } return this ; } } let callback = function ( ) { console .log ('you are so nice' ); } let pubsub = new PubSub ();pubsub.on ('completed' , (...args ) => { console .log (args.join (' ' )); }).on ('completed' , callback); pubsub.emit ('completed' , 'what' , 'a' , 'fucking day' ); pubsub.off ('completed' , callback); pubsub.emit ('completed' , 'fucking' , 'again' );
状态模式(State) 在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。状态模式:对象行为是基于状态来改变的。
优缺点
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 var trafficLight = (function ( ) { var currentLight = null ; return { change : function (light ) { currentLight = light; console .log (currentLight) ---对应各自的实例对象 currentLight.go (); ---实例对象的方法 } } })(); function RedLight ( ) { }RedLight .prototype .go = function ( ) { console .log ("this is red light" ); } function GreenLight ( ) { }GreenLight .prototype .go = function ( ) { console .log ("this is green light" ); } function YellowLight ( ) { }YellowLight .prototype .go = function ( ) { console .log ("this is yellow light" ); } trafficLight.change (new RedLight ()); trafficLight.change (new YellowLight ()); function Menu ( ) { }Menu .prototype .toggle = function (state ) { state (); } var menuStates = { "show" : function ( ) { console .log ("the menu is showing" ); }, "hide" : function ( ) { console .log ("the menu is hiding" ); } }; var menu = new Menu ();menu.toggle (menuStates.show ); menu.toggle (menuStates.hide );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 const FSM = (() => { let currenState = "download" ; return { download : { click : () => { console .log ("暂停下载" ); currenState = "pause" ; }, del : () => { console .log ("先暂停, 再删除" ); } }, pause : { click : () => { console .log ("继续下载" ); currenState = "download" ; }, del : () => { console .log ("删除任务" ); currenState = "deleted" ; } }, deleted : { click : () => { console .log ("任务已删除, 请重新开始" ); }, del : () => { console .log ("任务已删除" ); } }, getState : () => currenState }; })(); class Download { constructor (fsm ) { this .fsm = fsm; } handleClick ( ) { const { fsm } = this ; fsm[fsm.getState ()].click (); } hanldeDel ( ) { const { fsm } = this ; fsm[fsm.getState ()].del (); } } let download = new Download (FSM );download.handleClick (); download.handleClick (); download.hanldeDel (); download.handleClick (); download.hanldeDel ();
策略模式(Strategy) 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。策略模式就是能够把一系列“可互换的”算法封装起来,并根据用户需求来选择其中一种。
**策略模式实现的核心就是:将算法的使用和算法的实现分离。**算法的实现交给策略类。算法的使用交给环境类,环境类会根据不同的情况选择合适的算法。
优缺点
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var calculateBouns = function (salary,level ) { if (level === 'A' ) { return salary * 4 ; } if (level === 'B' ) { return salary * 3 ; } if (level === 'C' ) { return salary * 2 ; } }; console .log (calculateBouns (4000 ,'A' )); console .log (calculateBouns (2500 ,'B' ));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var tween = { linear : function (t, b, c, d ){ return c*t/d + b; }, easeIn : function (t, b, c, d ){ return c * ( t /= d ) * t + b; }, strongEaseIn : function (t, b, c, d ){ return c * ( t /= d ) * t * t * t * t + b; }, strongEaseOut : function (t, b, c, d ){ return c * ( ( t = t / d -1 ) * t * t * t * t + 1 ) + b; }, sineaseIn : function (t, b, c, d ){ return c * ( t /= d ) * t * t + b; }, sineaseOut : function (t, b, c, d ){ return c * ( ( t = t / d -1 ) * t * t + 1 ) +b; } };
模板方法模式(Template Method) 在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。模板模式是抽象父类定义了子类需要重写的相关方法。 而这些方法,仍然是通过父类方法调用的。
模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
优缺点
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
js实现 ES6实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Animal { constructor ( ) { this .live = () => { this .eat (); this .sleep (); }; } eat ( ) { throw new Error ("模板类方法必须被重写" ); } sleep ( ) { throw new Error ("模板类方法必须被重写" ); } } class Dog extends Animal { constructor (...args ) { super (...args); } eat ( ) { console .log ("狗吃粮" ); } sleep ( ) { console .log ("狗睡觉" ); } } class Cat extends Animal { constructor (...args ) { super (...args); } eat ( ) { console .log ("猫吃粮" ); } sleep ( ) { console .log ("猫睡觉" ); } } let dog = new Dog ();dog.live (); let cat = new Cat ();cat.live ();
TS实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public abstract class Beverage { init ():void { this .boilWater () this .brew () this .pourInCup () this .addCondiments () } boilWater ():void { console .log ("把水煮沸" ) } abstract brew ():void abstract addCondiments ():void abstract pourInCup ():void } public class Coffee extends Beverage { brew ():void { console .log ("用沸水冲泡咖啡" ) } pourInCup ():void { console .log ("把咖啡倒进杯子" ) } addCondiments ():void { console .log ("加糖和牛奶" ) } } public class Tea extends Beverage { brew ():void { console .log ("用沸水浸泡茶叶" ) } pourInCup ():void { console .log ("把茶倒进杯子" ) } addCondiments ():void { console .log ("加柠檬" ) } } Beverage coffee = new Coffee () coffee.init () Beverage tea = new Tea () tea.init ()
钩子方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class Beverage { init (): void { this .boilWater () this .brew () this .pourInCup () if (this .customerWantsCondiments () ){ this .addCondiments (); } } boilWater (): void { console .log ("把水煮沸" ) } brew ( ) { throw new Error ('子类必须重写brew方法' ) } pourInCup ( ) { throw new Error ('子类必须重写pourInCup方法' ) } customerWantsCondiments ():boolean { return true } addCondiments ( ) { throw new Error ('子类必须重写addCondiments方法' ) } } class Coffee extends Beverage { brew ( ) { console .log ("用沸水冲泡咖啡" ) } pourInCup ( ){ console .log ("把咖啡倒进杯子" ) } customerWantsCondiments ( ) { return window .confirm ( '请问需要调料吗?' ); } addCondiments ( ) { console .log ( '加糖和牛奶' ); } } Coffee coffee = new Coffee ()coffee.init ()
访问者模式(Visitor) 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
优缺点
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
js实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var Visitor = (function ( ) { return { splice : function ( ){ var args = Array .prototype .splice .call (arguments , 1 ) return Array .prototype .splice .apply (arguments [0 ], args) }, push : function ( ){ var len = arguments [0 ].length || 0 var args = this .splice (arguments , 1 ) arguments [0 ].length = len + arguments .length - 1 return Array .prototype .push .apply (arguments [0 ], args) }, pop : function ( ){ return Array .prototype .pop .apply (arguments [0 ]) } } })() var a = new Object ()console .log (a.length )Visitor .push (a, 1 , 2 , 3 , 4 )console .log (a.length )Visitor .push (a, 4 , 5 , 6 )console .log (a.length )Visitor .pop (a)console .log (a)console .log (a.length )Visitor .splice (a, 2 )console .log (a)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var Visitor = (function ( ) { return { splice : function ( ) { var args = Array .prototype .splice .call (arguments , 1 ); return Array .prototype .splice .apply (arguments [0 ], args); }, push : function ( ) { var len = arguments [0 ].length || 0 ; var args = this .splice (arguments , 1 ); arguments [0 ].length = len + arguments .length - 1 ; return Array .prototype .push .apply (arguments [0 ], args); }, pop : function ( ) { return Array .prototype .pop .apply (arguments [0 ]); } } })(); var a = new Object ();Visitor .push (a,1 ,2 ,3 ,4 );Visitor .push (a,4 ,5 ,6 );Visitor .pop (a);Visitor .splice (a,2 );
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com