在我最近的一个项目中,有一个模块是关于表单校验的需求,需求大致是验证输入不能空,不能够重复的字符串等等,检验的规则大概有10种,而我的同事为了检验这样的表单功能,则编写大量的if-else代码,伪代码如下:
if (value.length ===0) {
// todo
} else if (value.length < 6) {
//todo
} else if (.....) {
//todo
}
可以发现上面的代码十分简单,也非常实用,但是也存在明显的缺点:
- 包含了大量的if-else语句,这些语句覆盖了大量的逻辑。
- 上述代码缺乏弹性,如果增加了新的需求,则必须重复的添加else if。
- 逻辑的复用性很差,我如果是其它的模块也操作相似的逻辑,则必须全部都复制一遍过去,造成一种很臃肿和很恶心的代码。
为了解决以上存在的问题,我们就很自然联想到设计模式中的一种-策略模式:
策略模式的定义:
定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换。
又比如我们现在有如下的场景:
如果绩效为S的人,那么他的年终奖则是他工资的4倍,如果绩效为A的人,那么他的年终奖则是他工资的3倍,如果绩效为B的人,那么他的年终奖则是他工资的2倍。
那么我们就自然的想到编写以下的代码:
let calculateBonus = (level,salary)=>{
if (level === "S") {
return salary*4
}else if (level === "A") {
return salary*3
}else if (level === "B"){
return salary * 2
}
}
console.log(calculateBonus("A",200)) // 800
console.log(calculateBonus("B",200)) // 600
那么其实这种代码,在某种程度上来,看着是很蠢的,也不符合,现在前端高大上,逼格高的作风。所以我们需要把它给改造一下:
let strategies = {
"S": function (salary) {
return salary * 4
},
"A": function (salary) {
return salary * 3
},
"B": function (salary) {
return salary * 2
}
}
let calcteBonus = (level, salary) => {
return strategies[level](salary);
}
console.log(calcteBonus("S", 200)) // 800
console.log(calculateBonus("B", 200)) // 600
上面的代码,我们通过构造strategies 对象,来存放各个逻辑的函数。实际上在javascript语言中,函数也是对象,函数也可以作为value来输出。
通过以上的改造,我们可以看到代码的结构更加简洁了,else-if的语句也不存在了,瞬间感觉B格就高了很多。当然了,也不是说else-if不好,只是我们有更好实现这种需求的方式,应当选择更为简洁和聪明的方式,也方便日后的维护。
改造表单验证的代码:
我们回到一开始的表单需求,我们放弃大量elseif的方式,从而采用更为简洁和聪明的形式。假设我们现在有以下的需求为:
- 用户名不能为空
- 密码长度不能少于6位
- 手机号码必须符合格式
<body>
<form>
<p>
用户名:<input type="text" id="username">
</p>
<p>
密码:<input type="text" id="password">
</p>
<p>
电话:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按钮</button>
</p>
</form>
<script>
var oBtn = document.getElementById("btn");
var oUserName = document.getElementById("username");
var oPassword = document.getElementById("password");
var oTelphone = document.getElementById("telphone");
oBtn.onclick = function () {
if (oUserName.value === "") {
alert("输入不能为空");
return false;
}
if (oPassword.value.length < 6) {
alert("密码不能少于6位");
return false;
}
if (!/(^1[3|5|8[0-9]{9}]$)/.test(oTelphone.value)) {
alert("手机号码格式不正确!");
return false;
}
}
</script>
</body>
我们又自然就编写以上的代码,这样我们又无法避免写出了大量if-else的代码。所以,我们又需要把代码进行重构!
<body>
<form>
<p>
用户名:<input type="text" id="username">
</p>
<p>
密码:<input type="text" id="password">
</p>
<p>
电话:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按钮</button>
</p>
</form>
<script>
let oBtn = document.getElementById("btn");
let oUserName = document.getElementById("username");
let oPassword = document.getElementById("password");
let oTelphone = document.getElementById("telphone");
//校验规则的对象
let strategies = {
isNoEmpty: function (value, msg) {
if (value.length === 0) {
return msg;
}
},
minLength: function (value, length, msg) {
if (value.length < length) {
return msg;
}
},
isMobile: function (value, msg) {
if (!/(^1[3|5|8[0-9]{9}]$)/.test(value)) {
return msg;
}
}
}
// 构造检验类
let validataFun = function () {
var validator = new Validator();
// 添加检验规则
validator.add(oUserName, "isNoEmpty", "用户名不能为空!");
validator.add(oPassword, "minLength:6", "密码长度不能小于6!");
validator.add(oTelphone, "isMobile", "手机号码格式不正确!");
let msg = validator.start(); // 调用开始检验的方法
return msg;
}
function Validator() {
this.cache = []; // 保存检验规则的数组
}
// 通过原型挂载add方法
Validator.prototype.add = function (dom, rule, msg) {
let ary = rule.split(":");
this.cache.push(function () { // 把检验的函数push进数组
let strategy = ary.shift(); // 用户所操作的strategy,删除第一项的数据,并返回第一项的数据
ary.unshift(dom.value); // unshift向开头添加元素,会影响到原数组
ary.push(msg);
return strategies[strategy](...ary);
// return strategies[strategy].apply(dom, ary);
})
}
// 执行检验规则
Validator.prototype.start = function () {
// console.log(this.cache[0]());
for (let i = 0; i < this.cache.length; i++) {
let msg = this.cache[i]();
if (msg) {
return msg;
}
}
}
oBtn.onclick = function () {
let msg = validataFun();
if (msg) {
alert(msg); // 输出失败检验的信息
return false;
}
}
</script>
</body>
我们通过策略模式重构代码后,我们发现可以通过配置方式来改变,某一个input的检验规则,例如,我们把用户名输入不能少于10个字符,则可以把代码修改为:
validator.add(oUserName, "minLength:10", "用户名长度不能小于10!");
那么问题来了,我希望用户名不能为空的同时,还希望它的长度不能少于6位,那么上面的代码则又需要进行修改。我们期望的是按照以下的检验规则来进行校验:
validator.add(oUserName, [{
strategy: "isNoEmpty",
msg: "用户名不能为空!"
}, {
strategy: "minLength:6",
msg: "用户名不能少于6位!"
}])
validator.add(oPassword, "minLength:6", "密码长度不能小于6!");
validator.add(oTelphone, "isMobile", "手机号码格式不正确!");
这个时候add方法,第二个参数接受的是数组,那么我们就通过遍历这个数组,把检验规则都push进cache数组中。
<body>
<form>
<p>
用户名:<input type="text" id="username">
</p>
<p>
密码:<input type="text" id="password">
</p>
<p>
电话:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按钮</button>
</p>
</form>
<script>
let oBtn = document.getElementById("btn");
let oUserName = document.getElementById("username");
let oPassword = document.getElementById("password");
let oTelphone = document.getElementById("telphone");
//校验规则的对象
let strategies = {
isNoEmpty: function (value, msg) {
if (value.length === 0) {
return msg;
}
},
minLength: function (value, length, msg) {
if (value.length < length) {
return msg;
}
},
isMobile: function (value, msg) {
if (!/(^1[3|5|8[0-9]{9}]$)/.test(value)) {
return msg;
}
}
}
// 构造检验类
let validataFun = function () {
var validator = new Validator();
// 添加检验规则
// validator.add(oUserName, "isNoEmpty", "用户名不能为空!");
// validator.add(oPassword, "minLength:6", "密码长度不能小于6!");
// validator.add(oTelphone, "isMobile", "手机号码格式不正确!");
validator.add(oUserName, [{
strategy: "isNoEmpty",
msg: "用户名不能为空!"
}, {
strategy: "minLength:6",
msg: "用户名不能少于6位!"
}])
validator.add(oPassword, [{
strategy: "minLength:6",
msg: "密码不能少于6位"
}]);
validator.add(oTelphone, [{
strategy: "isMobile",
msg: "校验规则不正确!"
}]);
let msg = validator.start(); // 调用开始检验的方法
return msg;
}
function Validator() {
this.cache = []; // 保存检验规则的数组
}
// 通过原型挂载add方法
Validator.prototype.add = function (dom, rule, msg) {
for (let i = 0; i < rule.length; i++) {
let ary = rule[i].strategy.split(":");
this.cache.push(function () { // 把检验的函数push进数组
let strategy = ary.shift(); // 用户所操作的strategy,删除第一项的数据,并返回第一项的数据
ary.unshift(dom.value); // unshift向开头添加元素,会影响到原数组
ary.push(rule[i].msg);
return strategies[strategy](...ary);
// return strategies[strategy].apply(dom, ary);
})
}
}
// 执行检验规则
Validator.prototype.start = function () {
for (let i = 0; i < this.cache.length; i++) {
let msg = this.cache[i]();
console.log(msg);
if (msg) {
return msg;
}
}
}
oBtn.onclick = function () {
let msg = validataFun();
if (msg) {
alert(msg); // 输出失败检验的信息
return false;
}
}
</script>
</body>
通过策略模式,我们是可以很轻松去解决我们日常工作一些比较繁琐的逻辑,当你的代码,产生了大量的if-else语法的时候,可以尝试使用策略模式,那么你的代码就变得简洁和有B格。