拿生活场景举例,放假回家,学生可以选择乘坐火车、高铁、飞机等方式,每一种回家方式就是一种策略。
拿编程场景举例,实现一个功能,可以有多种不同的算法,那每种算法就是一种策略,这些算法可以随意替换,开发者可以从中挑选任意的算法去实现这一功能。
概念
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
应用场景举例
来看一个可以应用策略模式的场景:表单验证(微信小程序)
![](https://i-blog.csdnimg.cn/blog_migrate/fa32459ac3b8b7bde5be3f6b6903668b.jpeg)
这是管理端发布志愿活动的页面,针对该场景,需要进行对用户发布表单做验证,以下为最容易想到、最简单实现的方法
postActivity: function () {
if (this.data.eventName === '') {
Toast.fail('活动标题未填');
return
}
if (this.data.location === '') {
Toast.fail('活动地点未填');
return
}
if (this.data.pushLink === '') {
Toast.fail('推送链接未填');
return
}
if (this.data.sponsor === '') {
Toast.fail('主办方未填');
return
}
if (this.data.type === '') {
Toast.fail('活动类型未填');
return
}
if (this.data.startTime === '') {
Toast.fail('报名时间未填');
return
}
if (this.data.endTime === '') {
Toast.fail('报名截止时间未填');
return
}
//向后端发送请求代码省略
},
以上代码实现功能没有任何问题,但是缺点也很明显:
1 if语句太多,占大量篇幅,且逻辑几乎相同,一模一样格式的代码,没必要出现很多次
2 如果后续,客户要求新增逻辑,加一些其他输入,还得在postActivity函数内部再改
3 整段代码无复用性,一个项目中涉及到表单提交会有多处,就会有多处表单验证,上述代码仅实现了该页面的需要,如果其它页面逻辑与其相同,还得再写一份一样格式的代码(俗称cv)
实现策略模式下表单验证:
step1
定义策略对象,封装一系列算法或者业务逻辑(演示就不放太多方法了,不然看着容易眼花)
const strategies = {
isEmpty: function (value, message) {
if (value.length === 0) {
return message;
}
}
};
step2
定义环境,环境用来将用户的请求指派给策略对象中的某一个(如果不理解这一部分,可以先看step3)
class formValidation {
constructor() {
//保存校验规则
this.cache = [];
}
//用来添加校验规则的方法
add(value, rule, message) {
//用空函数将策略包装,推进cache中
this.cache.push(function () {
return strategies[rule](value, message);
})
}
//用来启动校验的方法
start() {
let msg;
this.cache.some(fn => {
msg = fn();
if (msg) {
return true;
}
})
this.cache = [];
return msg;
}
}
step3
使用,也就是用户的请求
postActivity: function () {
formValidation.add(this.data.eventName, 'isEmpty', '活动标题未填');
formValidation.add(this.data.location, 'isEmpty', '活动地点未填');
formValidation.add(this.data.pushLink, 'isEmpty', '推送链接未填');
formValidation.add(this.data.sponsor, 'isEmpty', '主办方未填');
formValidation.add(this.data.index, 'isEmpty', '活动类型未填');
formValidation.add(this.data.startTime, 'isEmpty', '报名时间未填');
formValidation.add(this.data.endTime, 'isEmpty', '报名截止时间未填');
const errMsg = formValidation.start();
if (errMsg) {
Toast.fail(errMsg);
return;
}
//向后端发送请求代码省略
},
附完整代码
utils/form-validation.js
//定义策略
const strategies = {
isEmpty: function (value, message) {
if (value.length === 0) {
return message;
}
}
};
//定义环境
class formValidation {
constructor() {
//保存校验规则
this.cache = [];
}
//用来添加校验规则的方法
add(value, rule, message) {
//用空函数将策略包装,推进cache中
this.cache.push(function () {
return strategies[rule](value, message);
})
}
//用来启动校验的方法
start() {
let msg;
this.cache.some(fn => {
msg = fn();
if (msg) {
return true;
}
})
this.cache = [];
return msg;
}
}
module.exports = {
formValidation: new formValidation()
}
add-activity.js
import {
formValidation
} from "../../utils/form-validation";
Page({
data: {},
postActivity: function () {
formValidation.add(this.data.eventName, 'isEmpty', '活动标题未填');
formValidation.add(this.data.location, 'isEmpty', '活动地点未填');
formValidation.add(this.data.pushLink, 'isEmpty', '推送链接未填');
formValidation.add(this.data.sponsor, 'isEmpty', '主办方未填');
formValidation.add(this.data.index, 'isEmpty', '活动类型未填');
formValidation.add(this.data.startTime, 'isEmpty', '报名时间未填');
formValidation.add(this.data.endTime, 'isEmpty', '报名截止时间未填');
const errMsg = formValidation.start();
if (errMsg) {
Toast.fail(errMsg);
return;
}
//向后端发送请求代码省略
}
})
以上仅实现了验证输入文本是否为空规则,如果业务有更多要求,可以继续在策略对象里添加其它方法。
结尾:
策略模式优点:
代码可复用在项目中其他地方,省去了很多的cv步骤;
支持开放——封闭原则,即当项目需求发生变动时,不修改项目原有的代码功能,而是扩展模块的功能,来实现新的需求;
利用了组合、委托、多态等技术和思想,有效避免了多重条件语句。
策略模式缺点:
要向使用的用户暴露策略类里面的多种策略。