什么是策略模式
在策略模式中定义了一系列算法,将每一个算法封装起来,并让他们可以互相替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为型模式。
策略模式的优点:
- 策略模式利用组合、委托和多态等技术思想,可以有效的避免多种条件选择语句
- 策略模式提供了对开放封闭原则的完美支持,将算法封装在独立的
strategy
中,使得它易于切换,易于理解,易于拓展 - 策略模式中的算法也可以服用在系统的其他地方,从而避免许多重复的复制黏贴工作
- 在策略模式利用组合和委托来让Context拥有执行算法的能力,这也是i继承一种更轻便的替代方按
缺点:
- 编写难度加大,代码量变多,这是最直观的一个缺点,但也算不上缺点,毕竟不能以代码量多少衡量代码质量
- 首先,使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在
Content
中要好 - 其次要使用策略模式,必须了解所有的
strategy
,必须了解各个strategy
之间的不同点,这样才能选择一个合适的strategy
。这是违反最少知识原则的
现在我们有一个表单校验需求,在提交按钮之前,有如下几条校验规则:
- 账号不能为空
- 密码长度最少7位
一开始我可能会这么写
<template>
<div>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号">
<el-input v-model="form.account"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="textarea" v-model="form.passowrd"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
form: {
account: '',
passowrd: ''
},
}
},
methods:{
onSubmit(){
let {account,passowrd} = this.form;
if(!account){
this.$message.error('请输入账号')
}else if(passowrd.length<7){
this.$message.error('密码长度不能低于7位')
}else{
this.$message.success('表单校验成功')
}
}
}
}
</script>
这是一种很常见的编码方式,但它有很明显的缺点:
- 包含了很多if语句,这些语句要覆盖所有的校验规则。
- 若校验规则有变,需要改动
onSubmit
校验规则,违反开闭原则 - 算法复用性差
下面让我们使用策略模式重构表单校验
<template>
<div>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号">
<el-input v-model="form.account"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="textarea" v-model="form.passowrd"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
form: {
account: '',
passowrd: ''
},
strategies:{
isNoEmpty(data, key){
return new Promise((resolve,reject)=>{
if(data[key]){
resolve()
}else{
reject('请输入账号')
}
})
},
minLength(data,key){
return new Promise((resolve,reject)=>{
if(data[key].length<7){
reject('密码最低不能少于7个字')
}else{
resolve()
}
})
}
}
}
},
methods:{
onSubmit(){
let {strategies} = this;
strategies.isNoEmpty(this.form,'account')
.then(()=>strategies.minLength(this.form,'passowrd'))
.then(()=>{
this.$message.success('表单校验成功')
}).catch(err=>{
this.$message.error(err)
})
}
}
}
</script>
让我们把上面代码优化下,实现简单的element
表单校验
<template>
<div>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号">
<el-input v-model="form.account"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input type="textarea" v-model="form.passowrd"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
form: {
account: '',
passowrd: ''
},
rules:{
account:[
{methodName:"isNoEmpty",message:'请输入账号'}
],
passowrd:[
{methodName:"isNoEmpty",message:'请输入密码'},
{methodName:"minLength",length:7,message:'密码最低不能少于7位'}
],
},
strategies:{
minLength(val,err,length){
if(val.length<length){
return err;
}
},
isNoEmpty(val,err){
if(!val){
return err;
}
}
}
}
},
methods:{
strategiesFun(data){
let {strategies,rules} = this;
let err = []
for(let key in data) {
rules[key].forEach(item=>{
let _err = strategies[item.methodName](data[key],item.message,item.length);
_err && err.push(_err);
});
}
return new Promise((resolve,reject)=>{
err.length ? reject(err[0]) : resolve()
})
},
onSubmit(){
this.strategiesFun(this.form)
.then(()=>{
this.$message.success('表单校验成功')
}).catch(err=>{
this.$message.error(err)
})
}
}
}
</script>
比如,某天新增加一个需求,要求账号长度不小于5位数我们只需要在rules
对象中增加一条校验规则就行
rules:{
account:[
{methodName:"isNoEmpty",message:'请输入账号'},
{methodName:"minLength",length:5,message:'账号最低不能少于5位'}
],
passowrd:[
{methodName:"isNoEmpty",message:'请输入密码'},
{methodName:"minLength",length:7,message:'密码最低不能少于7位'}
],
},