相信有不少人在开发过程中都遇到一大串的if/else判断,代码又臭又长,而且随着需求的增加与产品的迭代,判断条件可能越来越长,越来越难以维护,有没有什么好的方式去解决这种弊端呢?答案是肯定的,策略模式是一种很好的解决办法,可以帮助我们重构规范业务代码,提高代码的可读性与可维护性。
首先,策略模式什么?
策略模式就是定义一系列的算法,把他们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对Context发起请求时,Context总是把请求委托给这些策略对象中间的某一个进行计算。
简单来说,就是将业务逻辑中公有的部分提取出来,封装成一个环境类Context,具体的业务实现过程封装在策略类strategies中,业务请求通过环境类去调用策略类中的实现过程,通过环境类给业务请求以响应消息,其中环境类充当一层代理,将相似的业务逻辑进行总结归纳整理,提高代码的可读性与可维护性。本文以表单验证为例,来介绍策略模式在实际开发中的使用。
策略模式最起码由两部分组成:
1、策略类
封装某个策略的具体实现方法
2、环境类Context
接收客户端业务处理请求,根据请求参数,调用策略类中具体的实现方法,策略类中必须要有处理方法;
以表单验证为例,其策略模式的实现过程如下:
function isFunction(fn) { return Object.prototype.toString.call(fn) === '[object Function]' } function isObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]' } /** * 策略实现类 */ const validateStrategies = { checkPattern: { mobileReg: /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[0-9]{1})|(18[0-9]{1})|(19[0-9]{1}))+\d{8})$/, numReg: /^[0-9]+(\.[0-9]{1,2})?$/ }, validateMobile(item) { let flag = true; if (item.data && !this.checkPattern.mobileReg.test(item.data)) { flag = false; } return flag; }, validateNumber(item) { let flag = true; if (!this.checkPattern.numReg.test(item.data)) { flag = false; } else if (item.point) { let pattern = new RegExp(`^[0-9]+(\.[0-9]{1,${item.point}})?$`); if (!pattern.test(item.data)) { flag = false; } } return flag; }, //校验字段是否必填 validateRequired(item) { let flag = true; if (!item.data) { flag = false; } else { flag = true; } return flag; } }; class ValidateForm { constructor() { this.cache = []; } add(rules) { if (Array.isArray(rules)) { this.cache = [...this.cache, ...rules]; } else if (isObject(rules)) { this.cache = [...this.cache, rules]; } else { messagee.error(`参数类型应该为Object或Array,但是却传入了${typeof rules}`, 2); } } remove(id) { let index = this.cache.findIndex(vv => vv.id && Object.is(vv.id, id)); this.cache.splice(index, 1); } start() { for (let i = 0, len = this.cache.length; i < len; i++) { let item = this.cache[i]; //传入的验证方法必须是一个function if (!isFunction(validateStrategies[item.validateFun])) { message.error('表单校验参数格式错误', 2); return false; } else { let flag = validateStrategies[item.validateFun](item); if (!flag) { return flag; } } } return true; } } export default ValidateForm;
上述代码,我们声明了一个ValidateForm类和一个validateStrategies策略对象,策略对象中封装了一些表单验证方法,在实例化Validate表单验证类的时候,需要往表单验证类中注入特定的表单校验规则,然后调用下启动方法,就能得到表单验证结果,以上代码使用过程如下:
* 1、let validate = new ValidateForm(); * 2、加入验证规则 validate.add(rules:Array<Object>) * 3、开始校验 validate.start() * @param rules:Array<Object> { * validateFun: '', //校验方法 * data: '',//校验的数据 * maxSize: '', //最大值 * ponit: '' //小数点 * ... * }
其中validateFun对应的是策略类中具体的方法名,其余参数可以根据业务需求酌情添加。饶了一大圈,这么做的好处是什么呢?
1、易维护扩展
如表单验证,在添加验证规则时,只需要加入一个具体的策略实现方法即可,不需要额外改动其他业务代码
2、可读性较高
熟悉某个具体功能逻辑不需要看一些不相关的内容
3、代码复用性较高
利用排列组合的方式,可以提高代码的复用性