发布于 2024 年 2 月 26 日,星期一
面试中常见的技术问题与候选人的实际经验之间的差距。策略模式是一种设计模式,用于在运行时选择算法或行为,使代码更具灵活性和可维护性。面试官询问候选人是否使用过策略模式,候选人回答没有,这可能表明候选人在实际项目中缺乏对高级设计模式的应用经验。这种情况在技术面试中很常见,反映了候选人的技术深度和广度。面试官通过此类问题评估候选人的设计能力、解决问题的思维方式以及对软件工程原则的理解。候选人若能展示对策略模式的理解,即使没有实际使用经验,也能体现其学习能力和对技术的敏感度。
策略模式:定义一系列的算法,将他们一个个封装,并使他们可相互替换。
/**
*
* @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); // 60000calcBonus('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); // 60000calcBonus('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); // 60000calcBonus('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 接口实现某种算法曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总Q:(question)
R:(result)
A:(attention matters)
D:(detail info)
S:(summary)
Ana:(analysis)
T:(tips)