最近在看《设计模式和开发实践》这本书,记录一下;
html
部分
<form id="form">
<label for="username">账号:</label><input id="username" type="text"><br>
<label for="password">密码:</label><input id="password" type="password"><br>
<label for="phonenum">手机:</label><input id="phonenum" type="text"><br>
<input id="submit" type="button" value="提交">
</form>
<p id="warn"></p>
我们先来看一下未用装饰者模式和策略模式实现的表单验证:
var form = document.getElementById('form'),
warn = document.getElementById('warn');
var formSubmit = function () {
if (form.username.value === '') {
return warn.textContent = '账号不能为空';
}
if (form.password.value === '') {
return warn.textContent = '密码不能为空';
}
if (form.phonenum.value === '') {
return warn.textContent = '手机号不能为空';
}
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg); ajax提交数据略
return warn.textContent = '用户信息已成功提交至服务器';
}
form.submit.onclick = function () {
formSubmit();
}
我们先来看一下上面的 formSubmit
方法:
formSubmit
在此承担了两个职责:
- 验证用户输入合法性;
- 提交
ajax
请求;
这种写法一来会造成函数臃肿,职责混乱,二来谈不上任何可复用性。
我们先把 formSubmit
验证用户输入 和 提交ajax
请求的代码,进行分离,把验证用户输入的逻辑放到 validata
函数中,并约定当 validata
验证未通过的时候,返回 false
,表示校验未通过,代码如下:
var validata = function () {
if (form.username.value === '') {
warn.textContent = '账号不能为空';
return false;
}
if (form.password.value === '') {
warn.textContent = '密码不能为空';
return false;
}
if (form.phonenum.value === '') {
warn.textContent = '手机号不能为空';
return false;
}
}
var formSubmit = function() {
if (validata() === false) {
return;
}
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg); ajax提交数据略
return warn.textContent = '用户信息已成功提交至服务器';
}
form.submit.onclick = function () {
formSubmit();
}
现在的代码已经有了一些改进,我们把校验逻辑放到了 validata
函数中,但 formSubmit
函数内部还要计算 validata
函数的返回值,并没有完全分离开来;
接下来我们用 装饰者模式 优化上面的这段代码,使 validata
和formSubmit
完全分离开来,如果 beforeFn
的执行结果返回 false
,表示不再执行后面的原函数;
那么问题来了,什么是装饰者模式呢?
装饰者模式定义:
在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责
,这种给对象动态地增加职责的方式称为 装饰者(decorator)模式
代码如下:
Function.prototype.before = function (beforeFn) {
var self = this;
return function () {
if (beforeFn.apply(this, arguments) === false) {
return;
}
return self.apply(this, arguments);
}
}
var validata = function () {
if (form.username.value === '') {
warn.textContent = '账号不能为空';
return false;
}
if (form.password.value === '') {
warn.textContent = '密码不能为空';
return false;
}
if (form.phonenum.value === '') {
warn.textContent = '手机号不能为空';
return false;
}
}
var formSubmit = function() {
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg); ajax提交数据略
return warn.textContent = '用户信息已成功提交至服务器';
}
formSubmit = formSubmit.before(validata);
form.submit.onclick = function () {
formSubmit();
}
在上面的这段代码中,校验输入和提交表单的代码完全分离出来,他们不再有任何耦合关系,formSubmit = formSubmit.before(validata)
这句代码如同把校验规则动态接在 formSubmit
函数之前,validata
成为一个即插即用的函数,它甚至可以被写成配置文件的形式,有利于我们分开维护这两个函数,再利用策略模式稍加改造,就可以把这些校验规则都写成插件的形式,用在不同的项目中。
装饰者模式 +
策略模式 实现表单验证
最终代码如下:
var form = document.getElementById('form'),
warn = document.getElementById('warn');
Function.prototype.before = function (beforeFn) {
var self = this;
return function () {
if (beforeFn.apply(this, arguments) === false) {
return;
}
return self.apply(this, arguments);
}
}
var vldStrategy = { //策略对象-验证规则
isNonEmpty: function (value, warnMsg) { //输入不为空
if (value === '') {
return warnMsg;
}
},
isLongEnough: function (value, length, warnMsg) { //输入足够长
if (value.length < length) {
return warnMsg;
}
},
isShortEnough: function (value, length, warnMsg) { //输入足够短
if (value.length > length) {
return warnMsg;
}
},
isMobile: function (value, warnMsg) { //手机号验证
var reg = /^1[3|5|8][0-9]{9}$/;
if (!reg.test(value)) {
return warnMsg;
}
}
}
var Validator = function () { //环境类
this.rules = []; //数组用于存放负责验证的函数
};
Validator.prototype.add = function (domNode, ruleArr) { //添加验证规则
var self = this;
for (var i = 0, rule; rule = ruleArr[i++];) {
(function (rule) {
var strategyArr = rule.strategy.split(':'),
warnMsg = rule.warnMsg;
self.rules.push(function () {
var tempArr = strategyArr.concat();
var ruleName = tempArr.shift();
tempArr.unshift(domNode.value);
tempArr.push(warnMsg);
return vldStrategy[ruleName].apply(domNode, tempArr);
});
})(rule);
}
return this;
};
Validator.prototype.start = function () { //开始验证表单
for (var i = 0, vldFn; vldFn = this.rules[i++];) {
var warnMsg = vldFn();
if (warnMsg) {
warn.textContent = warnMsg;
return false;
}
}
}
var vld = new Validator();
vld.add(form.username, [
{
strategy: 'isNonEmpty',
warnMsg: '账号不能为空'
},
{
strategy: 'isLongEnough:4',
warnMsg: '账号不能小于4位'
},
{
strategy: 'isShortEnough:20',
warnMsg: '账号不能大于20位'
}
]).add(form.password, [
{
strategy: 'isNonEmpty',
warnMsg: '密码不能为空'
}
]).add(form.phonenum, [
{
strategy: 'isNonEmpty',
warnMsg: '手机号不能为空'
},
{
strategy: 'isMobile',
warnMsg: '手机号格式不正确'
}
]);
var formSubmit = function () {
var msg = {
username: form.username.value,
password: form.password.value,
phonenum: form.phonenum.value
}
//ajax('...', msg);
return warn.textContent = '用户信息已成功提交至服务器';
}
formSubmit = formSubmit.before(vld.start.bind(vld));
form.submit.onclick = function () {
formSubmit();
};