[JS设计模式]策略模式

296 阅读4分钟

前言

最近读了《JavaScript设计模式与开发实践》这本书,感觉收获很大。便试着写一下记录总结,希望也能对大家有帮助。本文所有代码默认为JavaScript。

说明

策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

设计思路

策略模式主要解决的是,在某个存在多分支的逻辑中,如何规范这些分支内容,可以使代码更容易维护管理。

举个例子:

const handler = function(condition){
  if(condition === 'a'){
  	// doing a...
  }else if(condition === 'b'){
  	// doing b...
  }else if(condition === 'c'){
  	// doing c...
  }
  // ....
}

观察上述代码,相信大家都会觉得“这不是很常见的业务代码吗?”。是的,这种代码在一般的业务开发中非常常见。不管是用if...else...还是switch。本质上都是一段逻辑中存在具体分支所导致的。

通常这种代码,如果分支数是有限的,像“上下左右”,“大于小于等于”等这种分支。在这种情况下,这样写是问题不大的,因为后续修改的可能性会小一点。

但如果这里的分支是不确定的,如分枝是根据类型来判断的,开发时可能的类型有3种,后期迭代之后类型又逐渐增加。这时候就不得不重新修改这里的代码,扩张分支逻辑。

代码框架

策略模式的目的就是将算法的使用与算法的实现分离开来,就是把逻辑和具体分支部分要做的事情分开。这就说明要实现策略模式,我们的代码至少要有部分:

  1. 具体的分支策略
  2. 逻辑执行环境

实现例子

接下来我们举个具体例子。假如现在需要通知用户,而手上联系用户的方式有几种。需要根据不同联系方式来通知用户。

最直接的写法会是:

function notifyUser(userInfo){
	const method = userInfo.method;
  
  if(method === 'phone'){
  	sendSMS(userInfo);
  }else if(method === 'email'){
  	sendEmail(userInfo);
  }else if(method === 'telephone'){
  	call(userInfo);
  }
}

根据上述内容,我们知道这种写法是不妥的。因为假如日后需要拓展通知方式,这里的逻辑就会不可避免的要修改。

要实现策略模式,我们首先要把环境和策略拆开。

class NotifyBySms{
  constructor(userInfo){
  	this.userInfo = userInfo;
  }
  notify(){
  	sendSMS(this.userInfo);
  }
}

class NotifyByEmail{
  constructor(userInfo){
  	this.userInfo = userInfo;
  }
  notify(){
  	sendEmail(this.userInfo);
  }
}

class NotifyByCall{
  constructor(userInfo){
  	this.userInfo = userInfo;
  }
  notify(){
  	call(this.userInfo);
  }
}

class Notify{
	constructor(userInfo,strategy){
    this.strategy = strategy;
  }
  notify(){
    this.strategy.notify();
  }
}

// 通知时
const notifyIns = new Notify(new NotifyByEmail(userInfo));

参考上方的代码,我们可以看到原来的逻辑被拆成了具体的分枝内容(也就是具体策略),和执行的环境,2个部分。这样的话原来已经写好的代码,在后期是不用修改的。如果之后出现要增加策略的情况,只要增加一个策略类,在调用时触发即可。

进一步优化

当然上方的例子只是一个策略模式的大致框架,我们在写JavaScript的时候,其实可以有更加巧妙的用法。

var strategy = {
	'phone':sendSMS,
  'email':sendEmail,
  'telephone':call
};

function notifyUser(userInfo){
	strategy[userInfo.method](userInfo);
};

利用一个对象存放策略,在执行代码时,利用js可以用变量取对象内容的特性就可以把一个策略模式的框架确定下来。之后增加策略,只需要修改strategy对象和在后续的userInfo中修改method即可。

实际运用例子

策略模式在开发中可以运用在很多场景下,下面就选取一个常见的场景作为例子——表单校验。

我们都知道表单校验有很多种校验要求,如“不能为空”,“长度不能超过xx”,“内容必须为数字”等等。这种常见由于校验策略是不确定的,因此很适合用策略模式实现。

var strategy = {
	'isNotEmpty':function(value,errorMsg){
  	....
  },
  maxLength:function(value,errorMsg){
  	....
  },
  isNumber:function(value,errorMsg){
  	....
  },
}

const form = [
  {
    value:xxx,
    strategy:'isNotEmpty',
    errorMsg
  },{
    value:xxx,
    strategy:'maxLength',
    errorMsg
  },{
    value:xxx,
    strategy:'isNumber',
    errorMsg
  },
]

const validate = function(form){
	for(const item of form){
  	const errorMsg = strategy[item.strategy](item.value,item.errorMsg);
    return errorMsg;
  }
}

总结

策略模式是整体的思想并不难理解,在JavaScript中甚至有很好的实现方式。但它却能为我们的代码带来很大的优化。带来的优化不仅在日后的代码维护阶段可以体现,甚至在开发大量策略的业务过程中也能为大家梳理思路。

参考

《JavaScript设计模式与开发实践》—— 曾探