JS模式之策略模式和外观模式

策略模式

在开发中,表单验证经常会碰到,而且会需要验证不同的值,不同的匹配条件;这时候可以采用策略模式来统一起来处理,策略模式支持在运行时选择算法。

我们可以创建一个具有validate方法的验证器对象。无论表单的具体类型是什么,该方法都将会被调用,并且总是返回相同的结果;

验证器实现:

要验证的数据:

1
2
3
4
5
6
var data = {
first_name = "Super",
last_name = "Man",
age: "unknown",
username: "o_O"
}

配置不同数据采用不同的验证方法:

1
2
3
4
5
validator.config = {
first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};

使用方法:

1
2
3
4
validator.validate(data);
if(validator.hasErrors()) {
console.log(validator.messages.join("\n"))
})

验证器算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
validator.types = {
isNonEmpty: {
validate(value) {
return value !== '';
},
instructions: 'the value cannot be empty'
},
isNumber: {
validate(value) {
return !isNaN(value);
},
instructions: 'the value can only be a valid number, e.g. 1, 3.24, or 2010'
},
isAlphaNum: {
validate(value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: 'the value can only contain characters and numbers, no special symbols'
}
}

验证器本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var validator = {
types: {},
messages: [],
config: {},
validate: function(data) {
var i, msg, type, checker, result_ok;
this.messages = [];
for(i in data) {
if(data.hasOwnProperty(i)) {
type = this.config[i];
checker = this.types[type];
if(!type) {
continue;
}
if(!checker) {
throw {
name: 'VAlidationError',
message: 'No handler to validate type' + type
}
}
result_ok = checker.validate(data[i])
if(!result_ok) {
msg = 'invalid value for *' + i + '*, ' + checker.instructions;
this.message.push(msg)
}
}
}
return this.hasErrors()
},
hasErrors: function() {
return this.message.length !== 0;
}
}

这样,我们就实现了策略模式的关于表单验证的一个实例。

外观模式

外观模式是一种简单的模式,它为对象提供了一个可供选择的接口。这是一种非常好的实践,可保持方法的简洁性并且不会使他们处理过多的工作。如果原来有许多接受多个参数的uber方法,相比而言,按照本实现方法,最终将会创建更多数量的方法。有时候两个或更多的方法可能普遍的被一起调用。在这种情况下,创建另一个方法包装重复的方法调用是非常有意义的。

看个实践场景:

当处理浏览器事件时,你有以下方法:

  • stopPropagation 中止事件以避免其冒泡上升到父节点
  • preventDefault 阻止浏览器执行默认动作

并不需要在程序中到处复制者两个方法,可以创建一个外观方法从而同时调用者两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var myevent = {
// ...
stop: function(e) {
// 其他
if(typeof e.preventDefault === 'function') {
e.preventDefault()
}
if(typeof e.stopPropagation === 'function') {
e.stopPropagation();
}
if(typeof e.returnValue === 'boolean') {
e.returnValue = false;
}
if(typeof e.cancelBubble === 'boolean') {
e.cancelBubble = true;
}
}
}