JavaScript 设计模式 策略模式
策略模式
策略模式是一种行为设计模式, 它能让你定义一系列算法, 把它们一个个封装起来, 并使它们可以相互替换。
假如你需要前往机场。你可以选择骑自行车、乘坐大巴或搭出租车。这三种出行策略就是广义上的“算法”,它们都能让你从家里出发到机场。你无需深入它们的内部实现细节,如怎么开大巴、公路系统如何确保你家到机场有通路等。你只需要了解这些策略的各自特点:所需要花费的时间与金钱,你就可以根据预算和时间等因素来选择其中一种策略
策略模式的组成
一个策略模式至少由两部分组成。
- 第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
- 第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。
背景
假设我们正在编写一个注册页面,在点击注册按钮之时,有如下几条校验逻辑:
- 用户名不能为空
- 密码长度不能少于 6 位
- 手机号码必须符合格式
常规写法:
const form = document.getElementById('registerForm');
form.onsubmit = function () {
if (form.userName.value === '') {
alert('用户名不能为空');
return false;
}
if (form.password.value.length < 6) {
alert('密码长度不能少于 6 位');
return false;
}
if (!/^1[3|5|8][0-9]{9}$/.test(form.phoneNumber.value)) {
alert('手机号码格式不正确');
return false;
}
...
}
这是一种很常见的代码编写方式,但它有许多缺点:
onsubmit
函数比较庞大,包含了很多if-else
语句,这些语句需要覆盖所有的校验规则。onsubmit
函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度从 6 改成 8,我们都必须深入obsubmit
函数的内部实现,这是违反开放-封闭原则
的。- 算法的复用性差,如果在项目中增加了另外一个表单,这个表单也需要进行一些类似的校验,我们很可能将这些校验逻辑复制得漫天遍野。
利用策略模式改写
// 1. 定义规则(策略),封装表单校验逻辑
const strategies = {
isNonEmpty: function (value, errMsg) {
if (value === '') {
return errMsg;
}
},
minLenth: function (value, length, errMsg) {
if (value.length < length) {
return errMsg;
}
},
isMobile: function (value, errMsg) {
if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
return errMsg;
}
}
}
// 2. Validator 类
class Validator {
constructor() {
this.cache = [];
}
add(dom, rule, errMsg) {
const arr = rule.split(':');
this.cache.push(() => {
const strategy = arr.shift();
arr.unshift(dom.value);
arr.push(errMsg);
return strategies[strategy].apply(dom, arr);
})
}
start() {
for (let i = 0; i < this.cache.length; i++) {
const msg = this.cache[i]();
if (msg) return msg;
}
}
}
定义环境类 Context ,进行表单校验,调用策略:
form.onsubmit = function () {
const validator = new Validator();
validator.add(form.userName, 'isNonEmpty', '用户名不能为空');
validator.add(form.password, 'minLength:6', '密码长度不能少于 6 位');
validator.add(form.phoneNumber, 'isMobile', '手机号码格式不正确');
const errMsg = validator.start();
if (errMsg) {
alert(errMsg);
return false;
}
}
策略模式优缺点
- 优点 :
- 可以有效地避免多重条件选择语句。
- 对
开放-封闭原则
完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。 - 可以使算法复用在系统的其他地方,避免许多重复的复制粘贴工作。
- 缺点 :
- 使用策略模式会在程序中增加许多策略类或策略对象
- 要使用策略模式,必须了解所有的 strategy,了解它们的不同点,我们才能选择一个合适的 strategy。这是违反
最少知识原则
的。
策略模式适合应用场景
当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。
当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。
如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。
当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论