Skip to content

Commit ce36b39

Browse files
committed
策略模式 校对完毕
1 parent d36dc03 commit ce36b39

File tree

1 file changed

+145
-148
lines changed

1 file changed

+145
-148
lines changed

chapter7.markdown

Lines changed: 145 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -488,132 +488,129 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
488488

489489
装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。
490490

491-
<a name="a13"></a>
492491
## 策略模式
493492

494493
策略模式允许在运行的时候选择算法。你的代码的使用者可以在处理特定任务的时候根据即将要做的事情的上下文来从一些可用的算法中选择一个。
495494

496-
使用策略模式的一个例子是解决表单验证的问题。你可以创建一个validator对象,有一个validate()方法。这个方法被调用时不用区分具体的表单类型,它总是会返回同样的结果——一个没有通过验证的列表和错误信息。
495+
使用策略模式的一个例子是解决表单验证的问题。你可以创建一个`validator`对象,有一个`validate()`方法。这个方法被调用时不用区分具体的表单类型,它总是会返回同样的结果——一个没有通过验证的列表和错误信息。
497496

498-
但是根据具体的需要验证的表单和数据,你代码的使用者可以选择进行不同类别的检查。你的validator选择最佳的策略来处理这个任务,然后将具体的数据检查工作交给合适的算法去做。
497+
但是根据具体的需要验证的表单和数据,你代码的使用者可以选择进行不同类别的检查。你的`validator`选择最佳的策略来处理这个任务,然后将具体的数据检查工作交给合适的算法去做。
499498

500-
<a name="a14"></a>
501499
### 数据验证示例
502500

503501
假设你有一个下面这样的数据,它可能来自页面上的一个表单,你希望验证它是不是有效的数据:
504502

505503
var data = {
506-
first_name: "Super",
507-
last_name: "Man",
508-
age: "unknown",
509-
username: "o_O"
504+
first_name: "Super",
505+
last_name: "Man",
506+
age: "unknown",
507+
username: "o_O"
510508
};
511509

512-
对这个例子中的validator,它需要知道哪个是最佳策略,因此你需要先配置它,给它设定好规则以确定哪些是有效的数据。
510+
对这个例子中的`validator`而言,它需要知道哪个是最佳策略,因此你需要先配置它,给它设定好规则以确定哪些是有效的数据。
513511

514512
假设你不需要姓,名字可以接受任何内容,但要求年龄是一个数字,并且用户名只允许包含字母和数字。配置可能是这样的:
515513

516514
validator.config = {
517-
first_name: 'isNonEmpty',
518-
age: 'isNumber',
519-
username: 'isAlphaNum'
515+
first_name: 'isNonEmpty',
516+
age: 'isNumber',
517+
username: 'isAlphaNum'
520518
};
521519

522-
现在validator对象已经有了用来处理数据的配置,你可以调用validate()方法,然后将任何验证错误打印到控制台上
520+
现在`validator`对象已经有了用来处理数据的配置,你可以调用`validate()`方法,然后将验证错误打印到控制台上
523521

524522
validator.validate(data);
525523
if (validator.hasErrors()) {
526-
console.log(validator.messages.join("\n"));
524+
console.log(validator.messages.join("\n"));
527525
}
528526

529527
它可能会打印出这样的信息:
530528

531529
Invalid value for *age*, the value can only be a valid number, e.g. 1, 3.14 or 2010
532530
Invalid value for *username*, the value can only contain characters and numbers, no special symbols
533531

534-
现在我们来看一下这个validator是如何实现的。所有可用的用来检查的逻辑都是拥有一个validate()方法的对象,它们还有一行辅助信息用来显示错误信息:
532+
现在我们来看一下这个`validator`是如何实现的。所有可用的用来验证的逻辑都是拥有一个`validate()`方法的对象,它们还有一行辅助信息用来显示错误信息:
535533

536-
// checks for non-empty values
534+
// 验证空值
537535
validator.types.isNonEmpty = {
538-
validate: function (value) {
539-
return value !== "";
540-
},
541-
instructions: "the value cannot be empty"
536+
validate: function (value) {
537+
return value !== "";
538+
},
539+
instructions: "the value cannot be empty"
542540
};
543541

544-
// checks if a value is a number
542+
// 验证数字
545543
validator.types.isNumber = {
546-
validate: function (value) {
547-
return !isNaN(value);
548-
},
549-
instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010"
544+
validate: function (value) {
545+
return !isNaN(value);
546+
},
547+
instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010"
550548
};
551549

552-
// checks if the value contains only letters and numbers
550+
// 验证是否只包含字母和数字
553551
validator.types.isAlphaNum = {
554-
validate: function (value) {
555-
return !/[^a-z0-9]/i.test(value);
556-
},
557-
instructions: "the value can only contain characters and numbers, no special symbols"
552+
validate: function (value) {
553+
return !/[^a-z0-9]/i.test(value);
554+
},
555+
instructions: "the value can only contain characters and numbers, no special symbols"
558556
};
559557

560-
最后,validator对象的核心是这样的
558+
最后,`validator`对象的核心是这样的
561559

562560
var validator = {
563561

564-
// all available checks
565-
types: {},
566-
567-
// error messages in the current
568-
// validation session
569-
messages: [],
570-
571-
// current validation config
572-
// name: validation type
573-
config: {},
574-
575-
// the interface method
576-
// `data` is key => value pairs
577-
validate: function (data) {
578-
579-
var i, msg, type, checker, result_ok;
580-
581-
// reset all messages
582-
this.messages = [];
583-
for (i in data) {
584-
585-
if (data.hasOwnProperty(i)) {
586-
587-
type = this.config[i];
588-
checker = this.types[type];
589-
590-
if (!type) {
591-
continue; // no need to validate
592-
}
593-
if (!checker) { // uh-oh
594-
throw {
595-
name: "ValidationError",
596-
message: "No handler to validate type " + type
597-
};
598-
}
599-
600-
result_ok = checker.validate(data[i]);
601-
if (!result_ok) {
602-
msg = "Invalid value for *" + i + "*, " + checker.instructions;
603-
this.messages.push(msg);
604-
}
605-
}
606-
}
607-
return this.hasErrors();
608-
},
609-
610-
// helper
611-
hasErrors: function () {
612-
return this.messages.length !== 0;
613-
}
562+
// 所有可用的验证类型
563+
types: {},
564+
565+
// 本次验证所有的错误消息
566+
messages: [],
567+
568+
// 本次验证的配置,格式为:
569+
// name: validation type
570+
config: {},
571+
572+
// 接口方法
573+
// `data` 是名值对
574+
validate: function (data) {
575+
576+
var i, msg, type, checker, result_ok;
577+
578+
// 重置所有的错误消息
579+
this.messages = [];
580+
for (i in data) {
581+
582+
if (data.hasOwnProperty(i)) {
583+
584+
type = this.config[i];
585+
checker = this.types[type];
586+
587+
if (!type) {
588+
continue; // 不需要验证
589+
}
590+
if (!checker) { // 没有对应的验证类型
591+
throw {
592+
name: "ValidationError",
593+
message: "No handler to validate type " + type
594+
};
595+
}
596+
597+
result_ok = checker.validate(data[i]);
598+
if (!result_ok) {
599+
msg = "Invalid value for *" + i + "*, " + checker.instructions;
600+
this.messages.push(msg);
601+
}
602+
}
603+
}
604+
return this.hasErrors();
605+
},
606+
607+
// 辅助方法
608+
hasErrors: function () {
609+
return this.messages.length !== 0;
610+
}
614611
};
615612

616-
如你所见,validator对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,每快你就会有一个非常好的验证类型的集合。然后在每个新的使用场景下你需要做的仅仅是配置validator然后调用validate()方法。
613+
如你所见,`validator`对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,那么很快你就会有一个非常好的验证类型的集合。然后在新的使用场景下使用时你需要做的仅仅是配置`validator`然后调用`validate()`方法。
617614

618615
<a name="a15"></a>
619616
## 外观模式
@@ -632,35 +629,35 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意
632629
这是两个有不同目的的相互独立的方法,他们也应该被保持独立,但与此同时,他们也经常被一起调用。所以为了不在应用中到处重复调用这两个方法,你可以创建一个外观方法来调用它们:
633630

634631
var myevent = {
635-
// ...
636-
stop: function (e) {
637-
e.preventDefault();
638-
e.stopPropagation();
639-
}
640-
// ...
632+
// ...
633+
stop: function (e) {
634+
e.preventDefault();
635+
e.stopPropagation();
636+
}
637+
// ...
641638
};
642639

643640
外观模式也适用于一些浏览器脚本的场景,即将浏览器的差异隐藏在一个外观方法下面。继续前面的例子,你可以添加一些处理IE中事件API的代码:
644641

645642
var myevent = {
646-
// ...
647-
stop: function (e) {
648-
// others
649-
if (typeof e.preventDefault === "function") {
650-
e.preventDefault();
651-
}
652-
if (typeof e.stopPropagation === "function") {
653-
e.stopPropagation();
654-
}
655-
// IE
656-
if (typeof e.returnValue === "boolean") {
657-
e.returnValue = false;
658-
}
659-
if (typeof e.cancelBubble === "boolean") {
660-
e.cancelBubble = true;
661-
}
662-
}
663-
// ...
643+
// ...
644+
stop: function (e) {
645+
// others
646+
if (typeof e.preventDefault === "function") {
647+
e.preventDefault();
648+
}
649+
if (typeof e.stopPropagation === "function") {
650+
e.stopPropagation();
651+
}
652+
// IE
653+
if (typeof e.returnValue === "boolean") {
654+
e.returnValue = false;
655+
}
656+
if (typeof e.cancelBubble === "boolean") {
657+
e.cancelBubble = true;
658+
}
659+
}
660+
// ...
664661
};
665662

666663
外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要工作相当长一段时间(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后使用新的API创建一个外观方法在旧的对象前面。使用这种方式,当你完全替换到旧的对象的时候,你只需要修改少量客户代码,因为新的客户代码已经是在使用新的API了。
@@ -852,48 +849,48 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后
852849
下面是proxy对象的代码:
853850

854851
var proxy = {
855-
ids: [],
856-
delay: 50,
857-
timeout: null,
858-
callback: null,
859-
context: null,
860-
makeRequest: function (id, callback, context) {
861-
// add to the queue
862-
this.ids.push(id);
863-
864-
this.callback = callback;
865-
this.context = context;
866-
867-
// set up timeout
868-
if (!this.timeout) {
869-
this.timeout = setTimeout(function () {
870-
proxy.flush();
871-
}, this.delay);
872-
}
873-
},
874-
flush: function () {
875-
876-
http.makeRequest(this.ids, "proxy.handler");
877-
878-
// clear timeout and queue
879-
this.timeout = null;
880-
this.ids = [];
881-
882-
},
883-
handler: function (data) {
884-
var i, max;
885-
886-
// single video
887-
if (parseInt(data.query.count, 10) === 1) {
888-
proxy.callback.call(proxy.context, data.query.results.Video);
889-
return;
890-
}
891-
892-
// multiple videos
893-
for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
894-
proxy.callback.call(proxy.context, data.query.results.Video[i]);
895-
}
896-
}
852+
ids: [],
853+
delay: 50,
854+
timeout: null,
855+
callback: null,
856+
context: null,
857+
makeRequest: function (id, callback, context) {
858+
// add to the queue
859+
this.ids.push(id);
860+
861+
this.callback = callback;
862+
this.context = context;
863+
864+
// set up timeout
865+
if (!this.timeout) {
866+
this.timeout = setTimeout(function () {
867+
proxy.flush();
868+
}, this.delay);
869+
}
870+
},
871+
flush: function () {
872+
873+
http.makeRequest(this.ids, "proxy.handler");
874+
875+
// clear timeout and queue
876+
this.timeout = null;
877+
this.ids = [];
878+
879+
},
880+
handler: function (data) {
881+
var i, max;
882+
883+
// single video
884+
if (parseInt(data.query.count, 10) === 1) {
885+
proxy.callback.call(proxy.context, data.query.results.Video);
886+
return;
887+
}
888+
889+
// multiple videos
890+
for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
891+
proxy.callback.call(proxy.context, data.query.results.Video[i]);
892+
}
893+
}
897894
};
898895

899896
了解代理模式后就在只简单地改动一下原来的代码的情况下,将多个web service请求合并为一个。

0 commit comments

Comments
 (0)