定义
策略模式(Strategy Pattern)属于行为型设计模式,旨在维护多分支复杂逻辑时,每一个分支对应的类只需要关心自己内部的实现逻辑即可,无需关心是如何切换策略的。简而言之,一句话判断走哪个类,这个执行自己的行为。
在传统的逻辑判断中,多一条分支会多写一条判断条件,当条件越来越多,就会产生大量if-else或者switch-case的灾难性代码块,如果新增一个新的条件,就需要在原逻辑上修改,严重违背了开闭原则。策略模式就是为了解决这个问题的,根本上就是要让实现逻辑和使用逻辑解耦分离。
常见应用场景
策略模式在代码设计中是一种常用的思想,在前端开发的例子中也很常见,如:
- 表单验证:用户只需要维护数据就可以使其内部实现包括但不限于邮箱、手机号、数字等校验逻辑。
- 风格切换:用户切换风格相当于切换策略,根据不同的策略调用不同的实现方法。
- 缓存调度:根据不同的策略去调用localStorage、sessionStorage、IndexDB等。
举例说明
公司标准产品中有国际化的需求,要求根据目前语言切换对应的语言包,这里以翻译为例。在中文模式下传入词条需要返回对应的中文文案,英文同理。
代码实现
策略模式与很多设计模式的思想类似,都是找了一个“中间人”做一些事情从而达到解耦的目的,策略模式有一个上下文Context类去设置策略和调用策略行为。
这里定义一个IStrategy接口,用来约束LangCn和LangEn两个类。Context类内部维护了一个成员strategy来存储当前策略,setSt暴露给客户端修改策略,executeStrategy暴露给客户端执行策略。LangCn和LangEn两个类只需要关注自己解析转换词条的逻辑就好。
对于代码而言,LangCn和LangEn内部模拟了各自的词条包,当然如果是真正的产品代码中,不能就这样写在内部,需要单独维护。
interface IStrategy { parseWord(str: string):string; } class LangCn implements IStrategy{ parseWord(str: string):string { // 模拟中文词条包 return { 'zxy.10001': '苹果', 'zxy.10002': '香蕉', 'zxy.10003': '橙子' }[str] || str; } } // 英文词条 class LangEn implements IStrategy{ parseWord(str: string):string { // 模拟英文词条包 return { 'zxy.10001': 'apple', 'zxy.10002': 'banana', 'zxy.10003': 'orange' }[str] || str; } } // 上下文内部维护具体策略的引用 回头客户端调用时 调用上下文提供的方法即可 class Context { private strategy: IStrategy; constructor(st: IStrategy) { this.strategy = st; } // 设置新策略 setSt(st: IStrategy) { this.strategy = st; } // 执行策略的方法 executeStrategy(str: string): string { return this.strategy.parseWord(str); } } // 客户端 // 创建一个上下文对象 const cnCtx = new Context(new LangCn()); const result = cnCtx.executeStrategy('zxy.10003'); console.log(result); const result1 = cnCtx.executeStrategy('zxy.10001'); console.log(result1);
客户端在创建上下文实例时,传入了LangCn的实例,因为在Context类的构造方法中初始化了传入的策略对象,那么这里就是初始化了中文的策略。 当使用cnCtx调用executeStrategy方法时,cnCtx内部成员strategy已经指向LangCn了,所以调用的parseWord也是LangCn中的。
前端代码优化
在前端代码review时,不难发现有许多if-else和switch-case是可以使用策略模式维护的,改动不大,但可以有效分离使用和实现的逻辑,比如使用映射表代替,这样在新增、修改、删除时只需要操作映射表即可,无需修改逻辑代码块,而且代码风格风味优雅。
// 用逻辑堆砌 不推荐 function doExecute(type) { if (type === 5) { return 'ALI_TYPE'; } else if (type === 8) { return 'TX_TYPE'; } else if (type === 11) { return 'APA_TYPE'; } else if (type === 14) { return 'HM_TYPE'; } return 'NORMAL_TYPE' } // 用映射表维护 推荐 const typeMap = { 5: 'ALI_TYPE', 8: 'TX_TYPE', 11: 'APA_TYPE', 14: 'HM_TYPE' }; function doExecute(type) { return typeMap[type] || 'NORMAL_TYPE'; }
总结
策略模式思想其实很简单,根本上就是将使用和实现逻辑分离,很多业务场景中有共性都可以使用这种方法论,也符合了单一职责和开闭原则的设计理念。前端必须掌握的设计模式系列到这里已经是最后一个模式了,如果对您有帮助希望多多点赞哦!