发布于 2024 年 2 月 26 日,星期一
策略模式是一种设计模式,它允许在运行时选择算法或行为,通过将这些算法或行为封装成独立的策略类,使得它们可以相互替换。在前端开发中,策略模式常用于处理复杂的条件逻辑,避免代码的重复和冗长。通过将不同的策略封装在独立的函数或对象中,可以提高代码的可维护性和可扩展性,同时减少代码的耦合度。这种模式特别适用于需要根据不同条件执行不同操作的场景,如表单验证、数据处理等。使用策略模式,开发者可以轻松地添加、修改或删除策略,而无需修改核心代码,从而实现代码的灵活性和可复用性。
<<<<<<< HEAD:WritingPlan/PublishedArticle/面试官:策略模式有使用过吗?我:没有.......md
=======
系列首发于公众号『非同质前端札记』https://mp.weixin.qq.com/s?__biz=MzkyOTI2MzE0MQ==&mid=2247485576&idx=1&sn=5ddfe93f427f05f5d126dead859d0dc8&chksm=c20d73c2f57afad4bbea380dfa1bcc15367a4cc06bf5dd0603100e8bd7bb317009fa65442cdb&token=1071012447&lang=zh_CN#rd ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。
d01511f06d20186737a35f54508e8dd2e2752882:WritingPlan/PublishedArticle/JavaScript程序设计模式小技巧——策略模式,快看快用!!!.md
策略模式:定义一系列的算法,将他们一个个封装,并使他们可相互替换。
/**
*
* @param {*} level 绩效等级
* @param {*} salary 工资基数
* @returns 年终奖金额
*/
var calcBonus = function (level, salary) {
if (level === "S") {
return salary * 4;
} else if (level === "A") {
return salary * 3;
} else if (level === "B") {
return salary * 2;
}
};
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
函数逻辑太多
缺乏弹性
,比如如果我们需要增加一个等级 C,那就必须要去修改 calcBonus 函数。这就违反了开放-封闭原则
。复用性差
。如果后续还要重用这个程序去计算奖金,我们只有去 C,V。var totalS = function (salary) {
return salary * 4;
};
var totalA = function (salary) {
return salary * 3;
};
var totalB = function (salary) {
return salary * 2;
};
var calcBonus = function (level, salary) {
if (level === "S") {
return totalS(salary);
} else if (level === "A") {
return totalA(salary);
} else if (level === "B") {
return totalB(salary);
}
};
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
将其定义为一系列的算法,将他们每一个封装起来,将不变的部分和变化的部分隔开。
算法的使用方式是不变的,都是根据某个算法获取最后的奖金金额。而在每个算法的内部实现却是不同的,每一个等级对应着不同的计算规则
。在策略模式程序中:最少由两部分组成,一部分是一组策略类,在策略类中封装了具体的算法,并负责具体的计算过程。一部分是环境类 context,接受用户的请求,并将请求委托给某一个策略类。
var strategies = {
S: function (salary) {
return salary * 4;
},
A: function (salary) {
return salary * 3;
},
B: function (salary) {
return salary * 2;
},
};
var calcBonus = function (level, salary) {
return strategies[level](salary);
}
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
策略模式的实现并不复杂,关键是如何从策略模式的实现背后,找到封装变化,委托和多态性这些思想的价值
。function submit() {
let { username, password, tel } = infoForm;
if (username === "") {
Toast("用户名不能为空");
return false;
}
if (password.length < 6) {
Toast("密码不能少于 6 位");
return false;
}
if (!/(^1[3|5|8][0-9]{9}$)/.test(tel)) {
Toast("手机号码格式不正确");
return false;
}
// .....
}
let infoForm = {
username: "我是某某某",
password: 'zxcvbnm',
tel: 16826384655,
};
var strategies = {
isEmpty: function (val, msg) {
if (!val) return msg;
},
minLength: function (val, length, msg) {
if (val.length < length) return msg;
},
isTel: function (val, msg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(val)) return msg;
},
};
var validFn = function () {
var validator = new Validator();
let { username, password, tel } = infoForm;
validator.add(username, "isEmpty", "用户名不能为空");
validator.add(password, "minLength:6", "密码不能少于 6 位");
validator.add(tel, "isTel", "手机号码格式不正确");
var msg = validator.start();
return msg;
};
class Validator {
constructor() {
this.cache = [];
}
add(attr, rule, msg) {
var ruleArr = rule.split(":");
this.cache.push(function () {
var strategy = ruleArr.shift();
ruleArr.unshift(attr);
ruleArr.push(msg);
return strategies[strategy].apply(attr, ruleArr);
});
}
start() {
for (let i = 0; i < this.cache.length; i++) {
var msg = this.cache[i]();
if (msg) return msg;
}
}
}
function submit() {
let msg = validFn();
if (msg) {
Toast(msg);
return false;
}
console.log('verify success');
// .....
}
submit();
validator.add(username, [
{
strategy: "isEmpty",
msg: "用户名不能为空"
},
{
strategy: 'minLength:6',
msg: '密码不能少于 6 位'
}
]);
let infoForm = {
username: "阿斯顿发生的",
password: "ss1sdf",
tel: 15829485647,
};
var strategies = {
isEmpty: function (val, msg) {
if (!val) return msg;
},
minLength: function (val, length, msg) {
if (val.length < length) return msg;
},
isTel: function (val, msg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(val)) return msg;
},
};
var validFn = function () {
var validator = new Validator();
let { username, password, tel } = infoForm;
validator.add(username, [
{
strategy: "isEmpty",
msg: "用户名不能为空",
},
{
strategy: "minLength:6",
msg: "密码不能少于 6 位",
},
]);
validator.add(password, [
{
strategy: "minLength:6",
msg: "密码不能少于 6 位",
},
]);
validator.add(tel, [
{
strategy: "isTel",
msg: "手机号码格式不正确",
},
]);
var msg = validator.start();
return msg;
};
class Validator {
constructor() {
this.cache = [];
}
add(attr, rules) {
for (let i = 0; i < rules.length; i++) {
var rule = rules[i];
var ruleArr = rule.strategy.split(":");
var msg = rule.msg;
var cacheItem = this.createCacheItem(ruleArr, attr, msg);
this.cache.push(cacheItem);
}
}
start() {
for (let i = 0; i < this.cache.length; i++) {
var msg = this.cache[i]();
if (msg) return msg;
}
}
createCacheItem(ruleArr, attr, msg) {
return function () {
var strategy = ruleArr.shift();
ruleArr.unshift(attr);
ruleArr.push(msg);
return strategies[strategy].apply(attr, ruleArr);
};
}
}
function submit() {
let msg = validFn();
if (msg) {
Toast(msg);
return false;
}
console.log("verify success");
// .....
}
submit();
Context(环境类)
:持有一个 Strategy 类的引用,用一个 ConcreteStrategy 对象来配置Strategy(环境策略类)
:定义了所有支持的算法的公共接口,通常是以一个接口或抽象来实现。Context 使用这个接口来调用其 ConcreteStrategy 定义的算法。ConcreteStrategy(具体策略类)
:以 Strategy 接口实现某种算法<<<<<<< HEAD:WritingPlan/PublishedArticle/面试官:策略模式有使用过吗?我:没有.......md
曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结=======
曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总Q:(question)
R:(result)
A:(attention matters)
D:(detail info)
S:(summary)
Ana:(analysis)
T:(tips)
d01511f06d20186737a35f54508e8dd2e2752882:WritingPlan/PublishedArticle/JavaScript程序设计模式小技巧——策略模式,快看快用!!!.md