Example: XHR Factory
现在在一个页面中常规的任务就是使用ajax(asynchronous request and xml)。由于用户浏览器的不同。你必须实例化一些不同的类中的其中之一以便能用来make a requst。如果你在你的代码中make多于一个的ajax request,有必要抽象这个对象创建的代码把他放到一个类中同时创建一个他实际上make the request的不同步骤的封装。一个简单的工厂在这种情况工作的非常到位。不论是创建一个xmlhttprequest或者是activexobject的实例,这中间的决定因素就是浏览器的属性:

ajaxhandler interface and SimpleHandler class
1 /*ajaxhandler interface */
2
3 var ajaxHandler=new Interface('ajaxHandler',['request','createXhrObject']);
4
5 /* SimpleHandler class*/
6
7 var SimpleHandler=function(){
8
9
10
11 }//implement ajaxHandler
12
13 SimpleHandler.prototype={
14
15 request: function(menthod,url,callback,postVars){
16
17 var xhr= this.createXhrObject();
18
19 xhr.onreadystatechange=function(){
20
21 if(xhr.readyState !==4){
22
23 return;
24
25 }
26
27 (xhr.status===200)? callback.success(xhr.responseText,xhr.responseXML):callback(xhr.status);
28
29
30
31 }
32
33 xhr.open(menthod,url,true);
34
35 if(menthod !=='POST'){
36
37 postVars=null;
38
39 }
40
41 xhr.send(postVars);
42
43 },
44
45 createXhrObject:function(){//factory method
46
47 var methods=[
48
49 function(){
50
51 return new XMLHttpRequest();
52
53 },
54
55 function(){
56
57 return new ActiveXObject('Msxml2.XMLHTTP');
58
59 },
60
61 function(){
62
63 return new ActiveXobject('Microsoft.XMLHTTP');
64
65 }
66
67 ];
68
69 for(var i=0,len=methods.length;i<len;i++){
70
71 try{
72
73 methods[i]();
74
75 }catch(e){
76
77 continue;
78
79 }
80
81 //if method[i]worked
82
83 this.createXhrObject=methods[i];//memoize the method
84
85 return methods[i];
86
87
88
89
90
91 }
92
93 //if reach this point ,none of the method work
94
95 throw new Error('The method can not create xhr object');
96
97 }
98
99
100
101 }
102
103
这个有用的的方法ruquest完成了发送请求和处理响应的所有步骤。他创建了一个XHR对象,设置这个对象,同时发送请求。最有趣的的部分就是创建XHR对象。
工厂方法(createXhrObject)返回一个XHR对象基于浏览器的环境。当第一次运行的时候。它将尝试三种不同的方式来创建一个XHR对象。当这里过程中发现以这种方式能创建该对象时,他返回创建的对象同时用创建该对象的函数重写他本身。这个新的函数就成了createXhrObject方法。这个技术称为(memoizing(弱弱的翻译为缓存)),能用来创建一个包含复杂计算的函数和方法以便不需要重复使用他们。所有的复杂的setup 代码只调用一次,当这个方法第一次执行的时候,在这之后。只有关于浏览器不同的代码才执行。举个例子。如果上面的代码是在一个实现XMLHttpRequest类的浏览器中。createXhrObject将非常有效,我们注意他的第二次执行。
createXhrObject:function(){return new XMLHttpRequset();}
Memoizing 能使你的代码更有效率因为所有的步骤和测试代码都只执行一次。工厂方法是一个封装这种类型代码完美的方式因为你能调用明确的知道返回的对象而不需要关注这个代码在哪个平台上运行。所有的复杂的环境变量在这段代码中集中到一个地方处理。使用simpleHandler类来make啊request现在相当的简单了。你能使用request方法发送一个异步 的请求:

instance of simpleHandler
1 Var myHandler=new SimpleHandler();
2
3 Var callback={
4
5 Success:function(responseText){alert(‘Success:’+responseText);},
6
7 Failure:function(statusCode){alert(‘Failure:’+statusCode)}
8
9 }
10
11 myHandler.request(‘GET’,’script.php’,callback);
12
13
Specialized Connection Objects
你能进一步的使用这个例子同时在两个地方使用工厂模式以便依据网络的状况创建一个专门的request对象。你已经使用简单的工厂设计模式创建了一个XHR对象。你能使用另外一个工厂来返回不同的处理类,这些类从SimpleHandler中继承属性和方法。
首先你要创建两个处理函数,QueuedHandler将确保在发送另外一个请求前所有的请求都已经成功。同时offlineHandler将存储请求如果ueser is not online。

QueuedHandler class
1 /* QueuedHandler class*/
2
3 var QueuedHandler=function(){//implement the ajax handler
4
5 this.queue=[];
6
7 this.requestInProgress= false;
8
9 this.retryDelay=5;//in second
10
11 }
12
13 extend(QueuedHandler,SimpleHandler);
14
15 QueuedHandler.prototype.request= function(method,url,callback,postVars,override){
16
17 if( this.requestInProgress && ! override){
18
19 this.queue.push({
20
21 method: method,
22
23 url:url,
24
25 callback: callback,
26
27 postVars: postVars
28
29 });
30
31 } else{
32
33 this.requestInProgress= true;
34
35 var xhr=this.CreateXhrObject();
36
37 var that=this;
38
39 xhr.onreadystatechange=function(){
40
41 if(xhr.readyState !==4){
42
43 return;
44
45 }
46
47 if(xhr.status===200){
48
49 callback.success(xhr.responseText,xhr.responseXML);
50
51 that.advanceQueue();
52
53 }else{
54
55 callback.failure(xhr.status);
56
57 setTimeout(function(){ that.request(method,url,callback,postVars);},that.retryDelay*1000);
58
59
60
61 }
62
63 };
64
65 xhr.open(method,url,true);
66
67 if(method !=="POST"){
68
69 postVars=null;
70
71 }
72
73 xhr.send(postVars);
74
75 }
76
77 };
78
79 QueuedHandler.prototype.advanceQueue=function(){
80
81 if(this.queue.length===0){
82
83 this.requestInProgress= false;
84
85 return;
86
87 }
88
89 var req= this.queue.shift();
90
91 this.request(req.method,req.url,req.callback,req.postVars,true);
92
93
94
95 };
96
97
QueuedHandler’s 的request方法看上去和SimpleHandlers相似,但是他首先检查一些属性以确保在允许一个新的xhr对象创建之前进程中没有其他的请求。它同样尝试任何一个没有成功的请求。在一个set Interval中循环到请求成功为止。

offlineHandler class
1 /*offlineHandler class*/
2
3 var offlineHandler=function(){//implement ajaxhandler
4
5 this.storedRequests=[];
6
7
8
9 }
10
11 extend(offlineHandler,SimpleHandler);
12
13 offlineHandler.prototype.request=function(method,url,callback,postVars){
14
15 if(XhrManger.isoffline())//store the requests untile we are online
16
17 {
18
19 this.storedRequests.push({
20
21 method:method,
22
23 url:url,
24
25 callback:callback,
26
27 postVars:postVars
28
29 });
30
31 }else{//call simpleHandler's request method if we are on line
32
33 this.flushStoredRequests();
34
35 offlineHandler.supperClass.request(method,url,callback,postVars);
36
37
38
39 }
40
41 };
42
43 offlineHandler.prototype.flushStoredRequests=function(){
44
45 for(var i=0,len=storedRequests.length;i<len;i++){
46
47 var req= storedRequests[i];
48
49 offlineHandler.supperClass.request(req.method,req.url,req.callback,req.postVars);
50
51 }
52
53 }
54
55
offlineHandler也和上面的QueuedHandler有相同之处。使用XhrMananger.isOffline方法(这是我们要进一步讨论的内容),确保user在允许创建请求之前是online的.尽管是调用SimpleHandler’s的方法。它同样执行所有的stored请求一旦他侦测到user is online.