JS设计模式-策略模式
定义 定义一系列的算法,把他们一个个封装起来,并且使他们可以互相替换。 优点
-
- 策略模式利用组合,委托和多态等技术思想,可以避免多重条件语句。
-
- 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的stragety中,使得它们易于切换,理解和扩展。
-
- 策略模式中算法也可以在其他地方复用,避免冗余代码。 缺点
- 使用策略模式会增加许多策略类或者策略对象。
- 要使用stragety,必须要了解所有stragety的细节。此时stragety向客户暴露了其实现,这是有违最少知识原则的。
1.使用策略计算评分内容
假设有如下需求:总评分为5分,评分为1分显示非常差,评分为2显示差,评分为3显示一般,评分为4显示好,评分为5显示非常好。你可能会很自觉写出如下代码:
function getStarText(level) {
if (level = 1) {
return '非常差'
} else if (level = 2) {
return '差'
} else if (level = 3) {
return '一般'
} else if (level = 4) {
return '好'
} else if (level = 5) {
return '非常好'
}
}
console.log(getStarText(2)); // 非常差
使用策略实现
let strategies = {
level1: function (level) {
return '非常差'
},
level2: function (level) {
return '差'
},
level3: function (level) {
return '一般'
},
level4: function (level) {
return '好'
},
level5: function (level) {
return '非常好'
}
}
function getStarText(level) {
return strategies['level' + level] ? strategies['level' + level](level) : '一般'
}
console.log(getStarText(4)); // 非常差
2.使用策略模式实现表单校验 表单验证是一个很常见的功能需求,假设你需要为一个网站编写注册模块。用户需要输入用户、密码、手机号码点击注册 按钮进行注册,在向后端发送请求,需要在前端校验合法法:用户名不能为空,密码不能为空,手机号码不能为空且手机号码必须符合正确的格式。 html代码如下:
<form id="js_register">
<p>用户名:<input type="text" name="username"></p>
<p>密 码:<input type="text" name="password"></p>
<p>手机号码:<input type="text" name="phone"></p>
<button>提交</button>
</form>
可能我们很容易就写出了以下代码:
var registerForm = document.getElementById('js_register');
registerForm.onsubmit = function() {
if (registerForm.username.value === '') {
alert('用户名不能为空');
return false;
}
if (registerForm.password.value === '') {
alert('密码不能为空');
return false;
}
if (registerForm.phone.value === '') {
alert('手机号码不能为空');
return false;
}
if (!/^1[3|5|8][0-9]{9}$/.test(registerForm.phone.value)) {
alert('手机号码格式不正确');
return false;
}
}
缺点: 1.registerForm.onsubmit函数比较庞大,包含了很多if-esle语句。 2.registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,我们都必须深入registerForm.onsubmit 函数的内部实现,这是违反开放-封闭原则的。 3.算法的复用性差,如果在程序中还有一个登录表单,这个表单也需要进行一些类似的校验,那么我们很可能随处都可见这些校验逻辑规则的复制。 下面,我们使用策略模式来实现表单校验:
// 封装策略对象
let rules = {
required: function (value, message) {
if (value === '') {
return message
}
},
isMobile: function (value, message) {
if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
return message
}
}
}
// 校验规则
function Validate() {
this.rules = [];
}
Validate.prototype = {
constructor: Validate,
add: function (element, rule) {
let _self = this;
for (let i = 0; i < rule.length; i++) {
(function (rule) {
let array = rule.type.split(':'),
error = rule.message;
_self.rules.push(function () {
// 取出用户指定的stragety
let stragety = array.shift();
// 将input的value放到参数列表最前面
array.unshift(element.value);
// 将errMsg放到参数列表最后面
array.push(error);
return rules[stragety].apply(element, array);
})
})(rule[i]);
}
},
start: function () {
for (let i = 0; i < this.rules.length; i++) {
let message = this.rules[i]();
if (message) {
return message
}
}
}
}
let registerForm = document.getElementById('js_register'),
validates = function () {
let validate = new Validate();
validate.add(registerForm.username, [
{type: 'required', message: '用户名不能为空'}
]);
validate.add(registerForm.password, [
{type: 'required', message: '密码不能为空'}
]);
validate.add(registerForm.phone, [
{type: 'required', message: '手机号码不能为空'},
{type: 'isMobile', message: '手机号码格式不正确'}
]);
let message = validate.start();
return message
};
validates();
registerForm.onsubmit = function () {
let message = validates();
if (message) {
alert(message);
return false
}
}