本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15
策略模式(Strategy):将定义的一组算法封装起来,使其相互之间可以替换,封装的算法具有一定独立性,不会随着客户端变化而变化。
结构上看,它与状态模式很像,也是在内部封装一个对象,然后通过返回的接口对象实现对内部对象的调用,不同点是,策略模式不需要管理状态、状态间没有依赖关系、策略之间可以相互替换、在策略对象内部保存的是相互独立的一些算法。
常在验证表单时使用:
var InputCheck = (function () {
var rule = {
notNull(value) {
return /\s+/.test(value) ? '请输入内容' : '';
},
number(value) {
return /^[0-9]+(\.[0-9]+)?$/.test(value) ? '': '请输入数字';
},
phone(value) {
return /^\d{3}\-\d{8}$|^\d{4}\-\d{7}$/.test(value) ? '' : '请输入正确的电话号码格式'
}
}
return {
check(type, value) {
return rule[type] ? rule[type](value) : '没有该类型的检测方法';
},
addRule(type, fn) {
rule[type] = fn;
}
}
})();
InputCheck.addRule('nickName', function(value) {
return /^[a-zA-Z]+$/.test(value) ? '' : '请输入合适的昵称';
})
const msg = InputCheck.check('nickName', 'cz');
console.log(msg) // ''
const msg2 = InputCheck.check('nickName', '陈z');
console.log(msg2) // 请输入合适的昵称
const msg3 = InputCheck.check('phone', '02110086');
console.log(msg3) // 请输入正确的电话号码格式
参考:https://www.cnblogs.com/chenwenhao/p/12354176.html
回顾一下策略模式的思想:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互转换。
这句话说的更详细一点就是:定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对Context发起请求的时候,Context总是把请求委托给这些策略对象中间的某一个进行计算。
“并且使它们可以相互替换”,这句话在很大程度上是相对于静态类型语言而言的。因为静态类型语言中有类型检查机制,所以各个策略类需要实现同样的接口。当它们的真正类型被隐藏在接口后面时,它们才能被相互替换。而在JavaScript这种“类型模糊”的语言中没有这种困扰,任何对象都可以被替换使用。因此,JavaScript中的“可以相互替换使用”变现为它们具有相同的目标和意图。
场景:绩效为A的人年终奖有4倍工资,绩效为B的有3倍,绩效为C的只有2倍。
// 初级版本
// if-else需要覆盖所有的逻辑分支
// 复用性差 如果在程序的其他地方需要重用这些计算奖金的算法
// 比如计算分红的时候,绩效为C的人分红的计算与年终奖的计算一致,只用到这里的算法C。绩效为A、B的人计算分红的方式则又是另外一种,这里就做不到只复用算法C了
// 耦合强,增加、删除、修改某一个算法的实现都需要在calculateBonus中去修改
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === 'A') {
return salary * 4;
}
if (performanceLevel === 'B') {
return salary * 3;
}
if (performanceLevel === 'C') {
return salary * 2;
}
};
calculateBonus('B', 20000); // 输出:40000
策略模式指的是定义一系列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外。策略模式的目的就是将算法的使用与算法的实现分离。
好处就是以后如果A、B、C三个绩效的计算方式改变了,只需要改对应的算法就可以了,不需要在上面的if-else
增加或修改逻辑。
var performanceA = function () {};
performanceA.prototype.calculate = function (salary) {
return salary * 4
}
var performanceB = function () {};
performanceB.prototype.calculate = function (salary) {
return salary * 3;
};
var performanceC = function () {};
performanceC.prototype.calculate = function (salary) {
return salary * 2;
};
var Bonus = function (salary, strategy) {
this.salary = salary; // 原始工资
this.strategy = strategy; // 绩效等级对应的策略对象
};
Bonus.prototype.getBonus = function () {
return this.strategy.calculate(this.salary);
};
var bonus = new Bonus(10000, new performanceA());
console.log(bonus.getBonus()); // 输出:40000
上面的代码是基于传统面向对象语言的模仿,代码变得更加清晰,各个类的职责更加鲜明。在JavaScript中由于其灵活性可以更简洁一点,函数也是对象,可以赋值给其他对象的属性,将所有的策略集中到一个策略对象中,策略对象的各个属性就对应各个算法。就可以 strategies.A
访问到对应的算法,而不需要立即调用。
var strategies = {
"A": function (salary) {
return salary * 4;
},
"B": function (salary) {
return salary * 3;
},
"C": function (salary) {
return salary * 2;
}
};
var calculateBonus = function (level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('A', 20000)); // 输出: 80000
优点:
策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。
策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
在策略模式中利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的替代方案。
当然,策略模式也有一些缺点,但这些缺点并不严重。
首先,使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在Context中要好。 其次,要使用策略模式,必须了解所有的strategy(策略的名字要通过API暴露),必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy。比如,我们要选择一种合适的旅游出行路线,必须先了解选 择飞机、火车、自行车等方案的细节。此时strategy要向客户暴露它的所有实现,这是违反最少知识原则的。