状态模式是一种行为软件设计模式.一个对象在其内部状态改变时会改变它的行为.各种状态和状态改变行为的操作是被独立封装的..
一般情况是:如果一个事物有多钟状态,我们用一个变量保存当前状态名(string),通过if-else
做状态的判断,然后进行状态的变更.
这样最大的问题是: 你需要将所有的状态和变更状态后的行为都放在一个方法里.这个方法会变得很臃肿和复杂.会变的难以阅读和维护.
状态模式:将各种状态和状态之间切换的行为封装在一起.只需要一个Context来触发状态变化,而状态变化后引起的行为操作Context不用处理.
状态模式的优点:
状态和行为关系封装在一起,增加新的状态和状态转换会很容易
对象代替字符串保存当前状态,状态的可切换一目了然
避免了Context无限膨胀
Context中的请求动作和状态类中封装的行为非常容易,它们独立变化而不互相影响
状态模式的缺陷:
逻辑分散在状态类中,虽然避免的条件分支但也导致了逻辑分散问题
新增了封装状态和行为变化的对象,增加了代码量.
例子 简版电灯开关例子
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 var Light = function ( ) { this .currState = FSM.off; this .button = null ; } Light.prototype.init = function ( ) { var button = document .createElement('button' ); var self = this ; button.innerHTML = '已关灯' ; this .button = document .body.appendChild('button' ); this .button.onclick = function ( ) { self.currState.buttonWasPressed.call(self); } }; var FSM = { off : { buttonWasPressed () { console .log('关灯' ); this .button.innerHTML = '下一次按我就是开灯' ; this .currState = FSM.off } }, on : { buttonWasPressed () { console .log('开灯' ); this .button.innerHTML = '下一次按我就是关灯' ; this .currState= FSM.off } } }; var light = new Light();light.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 var delegate = function (client, delegation ) { return { buttonWasPressed () { return delegation.buttonWasPressed.apply(client, arguments ); } } } var FSM = { off : { buttonWasPressed () { console .log('关灯' ); this .button.innerHTML = '下一次按我就是开灯' ; this .currState = this .onState; } }, on : { buttonWasPressed () { console .log('开灯' ); this .button.innerHTML = '下一次按我就是关灯' ; this .currState = this .offState; } } }; var Light = function ( ) { this .offState =delegate(this , FSM.off); this .onState = delegate(this , FSM.on); this .currState = this .offState; this .button = null ; } Light.prototype.init = function ( ) { var button = document .createElement('button' ), self = this ; button.innerHTML = '已关灯' ; this .button = document .body.appendChild(button); this .button.onclick = function ( ) { self.currState.buttonWasPressed() } }; var light = new Light();light.init();
状态模式和策略模式 状态模式和策略模式看起来很像.都有一个 Context负责接收请求,并转给具体方法执行.
但它们之间是有明显的区别的,策略模式中各个策略类是平行的,并不知道其他策略类的存在.而状态模式中封装的状态是知道其他状态存在的.
状态模式 - 上传程序 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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 window .external.upload = function (state ) { console .log(state); } var plugin =(function ( ) { var plugin = document .createElement('embed' ); plugin.style.display = 'none' ; plugin.type = 'application/txftn-webkit' ; plugin.sign = function ( ) { console .log('开始文件扫描' ); } plugin.pause = function ( ) { console .log('暂停文件上传' ); } plugin.uploading = function ( ) { console .log('开始文件上传' ); } plugin.done = function ( ) { console .log('文件上传完成' ); } document .body.appendChild(plugin); return plugin; })() var Upload = function (fileName ) { this .plugin = plugin; this .fileName = fileName; this .buttion1 = null ; this .button2 = null ; this .signState = new SignState(this ); this .uploadingState = new UploadingState(this ); this .pauseState = new PauseState(this ); this .doneState = new DoneState(this ); this .errorState = new ErrorState(this ); this .currState = this .signState; } Upload.prototype.init = function ( ) { var that = this ; this .dom = document .createElement('div' ); this .dom = innerHTML = ` <span>文件名称: ${this .fileName} </span> <button data-action="button1">扫描中</button> <button data-action="button2">删除</button> ` document .body.appendChild(this .dom); this .button1 = this .dom.querySelector('[data-action="button1"]' ) this .button2 = this .dom.querySelector('[data-action="button2"]' ) this .bindEvent(); } Upload.prototype.bindEvent = function ( ) { var self = this ; this .button1.onclick = function ( ) { self.currState.clickHandler1(); } this .button2.onclick = function ( ) { self.currState.clickHandler2(); } } Upload.prototype.sign = function ( ) { this .plugin.sign(); this .currState = this .signState; } Upload.prototype.uploading = function ( ) { this .button1.innerHTML = '正在上传,点击暂停' ; this .plugin.uploading(); this .currState = this .uploadingState; } Upload.prototype.pause = function ( ) { this .button1.innerHTML = '已暂停, 点击继续上传' ; this .plugin.pause(); this .currState = this .pauseState; } Upload.prototype.done = function ( ) { this .button1.innerHTML = '上传完成' ; this .plugin.done(); this .currState = this .doneState; } Upload.prototype.error = function ( ) { this .button1.innerHTML = '上传失败' ; this .currState = this .errorState; } Upload.prototype.del = function ( ) { this .plugin.del(); this .dom.parentNode.removeChild(this .dom); } var StateFactory = (function ( ) { var State = function ( ) {}; State.prototype.clickHandler1 = function ( ) { throw new Error ('子类必须重写父类的 clickHandler1方法' ); } State.prototype.clickHandler2 = function ( ) { throw new Error ('子类必须重写父类的 clickHandler2方法' ); } return function (param ) { var F = function (uploadObj ) { this .uploadObj = uploadObj; } F.prototype = new State(); for (var i in param) { F.prototype[i] = param[i] } return F; } })() var SignState = StateFactory({ clickHandler1 () { console .log('扫描中,点击无效……' ); }, clickHandler2 () { console .log('文件正在扫描中, 不能删除' ); } }) var UploadingState = StateFactory({ clickHandler1 () { this .uploadObj.pause(); }, clickHandler2 () { console .log('文件正在上传中, 不能删除' ); } }) var PauseState = StateFactory({ clickHandler1 () { this .uploadObj.uploading(); }, clickHandler2 () { this .uploadObj.del(); } }) var DoneState = StateFactory({ clickHandler1 () { console .log('文件上传完成,点击无效' ); }, clickHandler2 () { this .uploadObj.del(); } }) var ErrorState = StateFactory({ clickHandler1 () { console .log('文件上传失败,点击无效' ); }, clickHandler2 () { this .uploadObj.del(); } }) var uploadObj = new Upload('JavaScript 设计模式与开发实践' );uploadObj.init(); window .external.upload = function (state ) { uploadObj[state](); } window .external.upload('sign' );setTimeout (()=> { window .external.upload('uploading' ); }, 1000 ) setTimeout (() => { window .external.upload('done' ); })