这是我参与8月更文挑战的第 28 天,活动详情查看:8月更文挑战
设计模式,是前人总结的一些经典场景下的经典的解决方案。核心其实是一种思想,无关乎语言。
这个讲解的用例已经到位了,就不贴我的项目里 js 实例代码了
何为状态模式
在我们的程序中,经常会有这么一种情况,我们需要根据外部传入的状态,来选择该怎么做,而这种选择,会散落在代码的各个地方,并且,每次选择时做的事情的类型不一定相同,即无法直接为外部的一个公共函数
代码演示
经典场景复现
function out(type){
// ...
switch (type){
case 1:
// do first like this
break;
case 2:
// do first like that
break;
// ...
}
// .....
switch (type){
case 1:
// do second like this
break;
case 2:
// do second like that
break;
// ...
}
// ......
}
因为是涉及到状态,每次处理的时候,都需要使用上 switch case 或者是 if else if,而且一个条件选择后的实际处理函数,肯定不会是 do xxx 这种轻飘飘的一行,这些代码是嵌套在我们正常的一个业务处理当中,会使外层的函数变得非常膨胀,当然,也能封装成函数调用,只是,管理和维护是一个大的问题
代码优化 v1
或许,我们可以将 case 中的代码抽出来,使用函数命名进行约定,效果类似于
function() doFirst_1(){}
function() doFirst_2(){}
// ....
再次优化 v2
我们也可以用闭包的思路来改进上一步的代码,将相关状态的代码整合到一起
let doThings = function(){
return {
type_1: {
first(){},
second(){},
},
type_2: {
first(){},
second(){},
},
// 。。。
}
}();
然后,我们在 case 中用类似于 doThings.type_1.first() 的方式对方法进行调用
反思 v1-1
或者,上面的这种优化不对,我们直接将整个 switch 打包出来?
function first(type){
switch (type){
case 1:
// do first like this
break;
// ...
}
}
丑陋的代码不过是藏在了另一个地方罢了
套用设计模式 v3
先说结论,这种情况就和状态模式非常的契合,我们按照状态去维护对应的方法
v2 已经有这种雏形了,但是没有处理好 switch,那我们直接将状态的选择封装到这个函数的内部,并提供方法来支持状态切换
思路:
- 为每一个状态定义一个 object,在这个 object 中按照状态去实现对应的方法
- 维护一个状态映射到第一步定义的 object 上,因为有的状态定义不一定是
0, 1, 2... - 定义一个内部的状态变量
- 声明一个方法,支持外部来变更这个状态
- 暴露一个实例,让外部直接调用,这个实例对应外部指定的状态
let doThings = function(){
let type_1 = { // (1)
first(){},
second(){},
}, type_2 = {
first(){},
second(){},
};
let status = { // (2)
0: type_1,
1: type_2,
}
let innerType = 0; // (3)
return {
setType(type){ // (4)
innerType = type;
},
instance : status[innerType], // (5)
}
}
}();
调用这个状态方法的外部的函数,业务显得非常的单纯,切换当前状态,然后直接进行方法调用,代码结构就显得非常的简洁
function out(type = 0){
doThings.setType(type);
doThings.instance.first();
// 做一些其他的业务
doThings.instance.second();
// ...
}
我觉得这个是我总结的一个适用性较高的代码模版,工作中如果确实能够用上,直接照着这个模版进行编码就妥了
优点
在 v3 的代码中,如果我们需要扩展新的状态,需要进行如下 2 步操作
- 在
(1)的位置,添加一个新的状态实例,并去实现对应的操作方法 - 在
(2)的位置,建立新的映射关系
缺点
按原先的编码方式,如果我们需要为现在的状态,扩展新的操作方法 doThree1(), doThree2(),我们直接在对应的地方直接扩展一套 switch + func() 定义,这个操作非常直接
换成我们改造的模版模式,我们需要在 v3 的 (1) 位置,在每一个状态实例上添加一个对应的 three() 的实现,改造起来相对于旧编码方式应该会麻烦一点点,吧。。
总结
好的编码,应该是便于阅读和扩展,这种设计的优点体现在后续的迭代升级上,虽然老板眼里只看到程序能不能跑,要是这玩意大概率轮到自己维护的情况,给以后的自己留一点摸鱼的时间不香嘛