跳到主要内容

策略模式

介绍

一个基于策略模式的程序至少由两部分组成,第一个部分是一组策略类 Strategies(可变), 策略类封装类具体的算法,并负责具体的计算过程。第二个部分是环境类 Context(不变), Context 接收客户的请求,随后把请求委托给某一个策略类。

特点

优点

  • 利用组合、委托、多态的技术和思想,避免多重条件选择语句 if...else/switch...case;
  • 复用性更高,算法函数可在系统其它地方使用;
  • 支持设计模式 “开发-封闭原则“ ,算法封装在独立的 Strategy 中,易于维护和扩展;
  • 策略模式使用 “组合和委托” 来让 Context 拥有执行算法的能力,一种替换对象继承的可行方案

缺点

  • 增加了许多策略类或对象(开发人员职能划分明确,人员成本有所增加);
  • 必须了解各个 Strategy 的不同点,违反 “最少知识原则”(组长手底下有对应的开发人员,才不用自己那么苦逼)

场景

表单校验

:::details 点击查看代码

class Schema {
constructor(descriptor) {
this.descriptor = descriptor; // 传入定义的校验规则
}
// 拆分出一些更通用的规则,比如required(必填)、len(长度)、min/max(最值)等,可以尽可能地复用
handleRule(val, rule) {
const { key, params, message } = rule;
let ruleMap = {
required() {
return !val;
},
max() {
return val > params;
},
validator() {
return params(val);
},
};

let handler = ruleMap[key];
if (handler && handler()) {
throw message;
}
}

validate(data) {
return new Promise((resolve, reject) => {
let keys = Object.keys(data);
let errors = [];
for (let key of keys) {
const ruleList = this.descriptor[key];
if (!Array.isArray(ruleList) || !ruleList.length) continue;

const val = data[key];
for (let rule of ruleList) {
try {
this.handleRule(val, rule);
} catch (e) {
errors.push(e.toString());
}
}
}
if (errors.length) {
reject(errors);
} else {
resolve();
}
});
}
}
// 声明每个字段的校验逻辑
const descriptor = {
nickname: [
{ key: "required", message: "请填写昵称" },
{ key: "max", params: 6, message: "昵称最多6位字符" },
],
phone: [
{ key: "required", message: "请填写电话号码" },
{
key: "validator",
params(val) {
return !/^1\d{10}$/.test(val);
},
message: "请填写正确的电话号码",
},
],
};

// 开始对数据进行校验
const validator = new Schema(descriptor);
const params = { nickname: "", phone: "123000" };
validator
.validate(params)
.then(() => {
console.log("success");
})
.catch((e) => {
console.log(e);
});

:::

实现

const strategy = {
A: function (content) {
return "A" + content;
},
B: function (content) {
return "B" + content;
},
C: function (content) {
return "C" + content;
},
};
const handle = function (key, content) {
return strategy[key](content);
};
console.log(handle("A", "我是一颗"));
console.log(handle("B", "小小的石头"));