Skip to main content
 首页 » 编程设计

javascript工厂方法模式

2022年07月16日25sharpest
  1 <!doctype html> 
  2 <html lang="en"> 
  3 <head> 
  4     <meta charset="UTF-8"> 
  5     <title>工厂方法模式</title> 
  6 </head> 
  7 <body> 
  8  
  9 <script> 
 10  
 11 /** 
 12  * 工厂模式 
 13  * 
 14  *  一个类或对象中往往会包含内部的对象。在创建这种成员对象时,你可能习惯于使用常规方式,也即用new关键字和类构造函数。问题在于这会导致相关的两个类之间产生依赖性。本章将讲述一种有助于消除这两个类之间的依赖性的模式,它使用一个方法来决定究竟要实例化哪个具体的类。简单工厂模式另外使用一个类或者静态方法(通常是一个单体)来生成实例,复杂工厂模式则使用子类来决定一个成员变量应该是哪个具体的类的实例。 
 15  */ 
 16  
 17 /** 
 18  * 简单工厂 
 19  * 
 20  * 定义: 
 21  * 提供一个创建实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口,抽象类或具体类。 
 22  * 
 23  * 本质: 
 24  * 简单工厂方法的内部主要实现的功能是“选择合适的实现类”。本质是“选择实现”。 
 25  * 
 26  * 命名建议: 
 27  * 类名称建议为“模块名称+Factory”。 
 28  * 方法名称通常为“get+接口名称”或者是“create+ 
 29  * 接口名称” 
 30  */ 
 31  
 32 // 假设你想开几个自行车商店,每个店都有几种型号的自行车出售 
 33 /* BycycleShop class */ 
 34  
 35 var BicycleShop = function () { 
 36 }; 
 37 BicycleShop.prototype = { 
 38     sellBicycle: function (model) { 
 39         var bicycle; 
 40  
 41         switch (model) { 
 42             case 'The Speedster': 
 43                 bicycle = new Speedster(); 
 44                 break; 
 45             case 'The Lowrider': 
 46                 bicycle = new Lowrider(); 
 47                 break; 
 48             case 'The Comfort Cruiser': 
 49             default: 
 50                 bicycle = new ComfortCruiser(); 
 51         } 
 52         bicycle.assemble(); 
 53         bicycle.wash(); 
 54  
 55         return bicycle; 
 56     } 
 57 }; 
 58 /* 
 59  sellBicycle方法根据所要求的自行车型号用switch语句创建一个自行车的实例。各种型号的自行车实例可以互换使用,因为它们都实现了Bicycle接口. 
 60  接口在工厂模式中起着很重要的作用。如果不对对象进行某种类型检查以确保其实现了必须的方法,工厂模式带来的好处也就所剩无几了。在所有这些例子中,你可以创建一些对象并且对它们一视同仁,那是因为你可以确信它们都实现了同样一批方法。 
 61  */ 
 62  
 63 /* Speedster class */ 
 64 var Speedster = function () { 
 65     // implements Bicycle 
 66     //.. 
 67 }; 
 68 Speedster.prototype = { 
 69     assemble: function () { 
 70     }, 
 71     wash: function () { 
 72     }, 
 73     ride: function () { 
 74     }, 
 75     repair: function () { 
 76     } 
 77 }; 
 78  
 79 /* 要出售某种型号的自行车,只要调用sellBycycle方法即可 */ 
 80 var californiaCruisers = new BicycleShop(); 
 81 var yourNewBike = californiaCruisers.sellBicycle('The Speedster'); 
 82  
 83 /* 
 84  如果你想在供货目录中加入一款新车型又会怎么样呢?你得为此修改BicycleShop的代码,哪怕这个类的实际功能实际上并没有发生改变---依旧是创建一个自行车的新实例,组装它,清洗它,然后把它交给顾客。更好的解决办法是把sellBicycle方法中“创建新实例”这部分工作转交给一个简单工厂对象。 
 85  */ 
 86 /* BicycleFactory namespace */ 
 87 var BicycleFactory = { 
 88     createBicycle: function (model) { 
 89         var bicycle; 
 90  
 91         switch (model) { 
 92             case 'The Speedster': 
 93                 bicycle = new Speedster(); 
 94                 break; 
 95             case 'The Lowrider': 
 96                 bicycle = new Lowrider(); 
 97                 break; 
 98             /*--------------------------------*/ 
 99             //添加新车型 
100             case 'The Flatlander': 
101                 biccle = new Flatlander(); 
102                 break; 
103             /*--------------------------------*/ 
104             case 'The Comfort Cruiser': 
105             default: 
106                 bicycle = new AcmeComfortCruiser(); 
107         } 
108  
109         return bicycle; 
110     } 
111 }; 
112  
113 /* 
114  BicycleFactory是一个单体,用来把createBicycle方法封装在一个命名空间中。这个方法返回一个实现了Bicycle接口的对象,然后你可以照常对其进行组装和清洗。 
115  */ 
116 /* BicycleShop class, improved */ 
117 var BicycleShop = function () { 
118 }; 
119 BicycleShop.prototype = { 
120     sellBicycle: function (model) { 
121         var bicycle = BicycleFactory.createBicycle(model); 
122  
123         bicycle.assemble(); 
124         bicycle.wash(); 
125  
126         return bicycle; 
127     } 
128 }; 
129 /* 
130  BicycleFactory就是简单工厂的一个很好的例子。这种模式把成员对象的创建工作转交给一个外部对象。这个外部对象可以像本例中一样是一个简单的命名空间,也可以是一个类的实例。如果负责创建实例的方法的逻辑不会发生变化,那么一般用单体或静态类方法创建这些成员实例是合乎情理的。但如果你要提供几种不同品牌的自行车,那么更恰当的做法是把这个创建方法实现在一个类中,并从该类派生出一些子类。 
131  */ 
132 /* 
133  简单工厂能帮助我们真正地面向接口编程. 
134  */ 
135  
136 /* 
137  框架 
138  
139  框架就是能完成一定功能的半成品软件。 
140  
141  框架能干什么? 
142  
143  1.能完成一定功能,加快应用开发进度。 
144  2.给我们一个精良的程序架构。 
145  
146  框架和设计模式的关系 
147  
148  1.设计模式比框架更抽象。 
149  2.设计模式是比框架更小的体系结构元素。 
150  3.框架比设计模式更加特例化。(框架总是针对特定领域的,设计模式更加注重从思想上,方法上来解决问题,更加通用化) 
151  */ 
152  
153  
154 /** 
155  * 工厂方法模式 
156  * 
157  * 定义: 
158  * 定义一个用于创建对象的接口,让子类决定实例化哪个类,Factory Method使一个类的实例化延迟到其子类。 
159  * 
160  * 本质: 
161  * 延迟到子类来选择实现 
162  * 
163  * 真正的工厂模式与简单工厂模式的区别在于,它不是另外使用一个类或对象来创建自行车,而是使用一个子类。按照正式定义,工厂是一个将其成员对象的实例化推迟到子类中进行的类。 
164  * 
165  * 在工厂方法模式里面,客户端要么使用Creator对象,要么使用Creator创建的对象,一般客户不直接使用工厂方法,当然也可以直接把工厂方法暴露给客户端操作,但是一般不这么做。 
166  */ 
167  
168 /* 
169  我们可以把BicycleShop设计为抽象类,让子类根据各自的进货渠道实现其createBicycle方法。 
170  */ 
171 /* BicycleShop class (abstract) */ 
172 var BicycleShop = function () { 
173 }; 
174 BicycleShop.prototype = { 
175     sellBicycle: function (model) { 
176         var bicycle = this.createBicycle(model); 
177  
178         bicycle.assemble(); 
179         bicycle.wash(); 
180  
181         return bicycle; 
182     }, 
183     createBicycle: function (model) { 
184         throw new Error('Unsupported operation on an abstract class.'); 
185     } 
186 }; 
187  
188 /* 
189  这个类中定义了createBicycle方法,但真要调用这个方法的话,会抛出一个错误。现在BicycleShop是一个抽象类,它不能被实例化,只能用来派生子类。设计一个经销特定自行车生产厂家产品的子类需要扩展BicycleShop类,重定义其中的createBicycle方法。 
190  下面是两个子类的例子,其中一个子类代表的商店从Acme公司进货,而另一个则从General Products公司进货 
191  */ 
192 function extend(subClass, superClass) { 
193     function F() { 
194     } 
195  
196     F.prototype = superClass.prototype; 
197     subClass.prototype = new F(); 
198     subClass.prototype.constructor = subClass; 
199  
200     subClass.superclass = superClass.prototype; 
201  
202     if (superClass.prototype.constructor === Object.prototype.constructor) { 
203         superClass.prototype.constructor = superClass; 
204     } 
205 } 
206 /* AcmeBicycleShop class */ 
207 var AcmeBicycleShop = function () { 
208 }; 
209 extend(AcmeBicycleShop, BicycleShop); 
210 AcmeBicycleShop.prototype.createBicycle = function (model) { 
211     var bicycle; 
212  
213     switch (model) { 
214         case 'The Speedster': 
215             bicycle = new Speedster(); 
216             break; 
217         case 'The Lowrider': 
218             bicycle = new Lowrider(); 
219             break; 
220         case 'The Comfort Cruiser': 
221         default: 
222             bicycle = new ComfortCruiser(); 
223     } 
224     Interface.ensureImplements(bicycle, Bicycle); 
225     return bicycle; 
226 }; 
227  
228 /* GeneralProductsBicycleShop class */ 
229 var GeneralProductsBicycleShop = function () { 
230 }; 
231 extend(GeneralProductsBicycleShop, BicycleShop); 
232 GeneralProductsBicycleShop.prototype.createBicycle = function (model) { 
233     var bicycle; 
234  
235     switch (model) { 
236         case 'The Speedster': 
237             bicycle = new Speedster(); 
238             break; 
239         case 'The Lowrider': 
240             bicycle = new Lowrider(); 
241             break; 
242         case 'The Comfort Cruiser': 
243         default: 
244             bicycle = new ComfortCruiser(); 
245     } 
246  
247     return bicycle; 
248 }; 
249 /* 
250  这些工厂方法生成的对象都实现了Bicycle接口,所以在其他代码眼里它们完全可以互换,自行车的销售工作还是与以前一样,只是现在所开的商店可以是Acme或General Products自行车专卖店。 
251  */ 
252 var alecsCruisers = new AcmeBicycleShop(); 
253 var yourNewBike = alecsCruisers.sellBicycle('The Lowrider'); 
254  
255 var bobsCruisers = new GeneralProductsBicycleShop(); 
256 var yourSecondNewBike = bobsCruisers.sellBicycle('The Lowrider'); 
257  
258 /* 
259  增加对其他生产厂家的支持很简单。只要再创建一个BicycleShop的子类并重定义其createBicycle工厂方法即可。我们也可以对各个子类进行修改,以支持相关厂家其他型号的产品。这是工厂模式最重要的特点。对Bicycle进行一般性操作的代码可以全部写在父类BicycleShop中,而对具体的Bicycle对象进行实例化的工作则被留到子类中。一般性的代码被集中在一个位置,而个体性的代码则被封装在子类中。 
260  */ 
261  
262 /* 
263  工厂模式的适用场合 
264  
265  * 动态实现: 
266  如果需要像前面自行车的例子一样,创建一些用不同方式实现同一接口的对象,那么可以使用一个工厂方法或简单工厂对象来简化选择实现的过程。这种选择可以是明确进行的也可以是隐含的。这是js中使用工厂模式的最常见的原因。 
267  
268  * 节省设置开销: 
269  如果对象需要进行复杂并且批次相关的设置,那么使用工厂模式可以减少每种对象所需的代码量。如果这种设置只需要为特定类型的所有实例执行一次即可,这种作用尤其突出。把这种设置代码放到类的构造函数中并不是一种高效的做法,这是因为即使设置工作已经完成,每次创建新实例的时候这些代码还是会执行,而且这样做会把设置代码分散到不同的类中。工厂方法非常适合于这种场合。它可以在实例化所有需要的对象之前先一次性地进行设置。无论有多少不同的类都会被实例化,这种办法都可以让设置代码集中在一个地方。 
270  如果所用的类需要加载外部库的话,这尤其有用。工厂方法可以对这些库进行检查并动态加载那些未找到的库。这些设置代码只存在于一个地方,因此以后改起来也方便得多。 
271  
272  * 用许多小型对象组成一个大对象 
273  工厂方法可以用来创建封装了许多较小对象的对象。考虑一下自行车对象的构造函数。自行车包含着许多更小的子系统:车轮,车架,传动部件等。如果你不想让某个子系统与较大的那个对象形成强耦合,而是想在运行时从许多子系统中进行挑选的话,那么工厂方法是一个理想的选择。使用这种技术,某天你可以为售出的所有自行车配上某种链条,要是第二天找到另一种更中意的链条,可以改而采用这个新品种。 
274  
275  */ 
276  
277  
278 // 示例:XHR工厂 
279 /* 简单工厂非常适合这种场合,它可以用来根据浏览器能力的不同生成一个XMLHttpRequest或ActiveXObject实例 
280  */ 
281 /* AjaxHandler interface */ 
282 var AjaxHandler = new Interface('AjaxHandler', ['request', 'createXhrObject']); 
283  
284 /* SimpleHandler class */ 
285 var SimpleHandler = function () { 
286 };     // implements AjaxHandler 
287 SimpleHandler.prototype = { 
288     request: function (method, url, callback, postVars) { 
289         var xhr = this.createXhrObject(); 
290         xhr.onreadystatechange = function () { 
291             if (xhr.readyState !== 4) { 
292                 return; 
293             } 
294             ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 
295                 callback.success(xhr.responseText, xhr.responseXML) : 
296                 callback.failure(xhr.status); 
297         }; 
298         xhr.open(method, url, true); 
299         if (method !== 'POST') { 
300             postVars = null; 
301         } 
302         xhr.send(postVars); 
303     }, 
304     createXhrObject: function () {      // Factory method 
305         var methods = [ 
306             function () { 
307                 return new XMLHttpRequest(); 
308             }, 
309             function () { 
310                 return new ActiveXObject('MSXML2.XMLHTTP.3.0'); 
311             }, 
312             function () { 
313                 return new ActiveXObject('MSXML.XMLHTTP'); 
314             }, 
315             function () { 
316                 return new ActiveXObject('Microsoft.XMLHTTP'); 
317             } 
318         ]; 
319  
320         for (var i = 0, len = methods.length; i < len; i++) { 
321             try { 
322                 methods[i](); 
323             } catch (e) { 
324                 continue; 
325             } 
326             // if we reach this point,method[i] worked 
327             this.createXhrObject = methods[i];    // Memoize the method 
328             return methods[i](); 
329         } 
330  
331         // if we reach this point,none of the methods worked 
332         throw new Error('SimpleHandler:Could not create an XHR object.'); 
333     } 
334 }; 
335  
336 /* 
337  createXhrObject这个工厂方法根据当前环境的具体情况返回一个XHR对象。在首次执行时,它会依次尝试三种用于创建XHR对象的不同方法,一旦遇到一种管用的,它就会返回创建的对象并将自身改为用以创建那个对象的函数。这个新函数于是变成了createXhrObject方法,这种技术被称为memoizing,它可以用来创建存储着复杂计算的函数和方法,以免再次进行这种计算。所有那些复杂的设置代码只会在方法首次执行时被调用一次,此后就只有针对当前浏览器的代码会被执行。 
338  */ 
339  
340 // 用SimpleHandler类发起一部请求的过程 
341 var myHandler = new SimpleHandler(); 
342 var callback = { 
343     success: function (responseText) { 
344         alert('Success:' + responseText); 
345     }, 
346     failure: function (statusCode) { 
347         alert('Failure:' + statusCode); 
348     } 
349 }; 
350 myHandler.request('GET', 'script.php', callback); 
351  
352  
353 // 专用型连接对象 
354 /* 
355  这个例子可以进一步扩展,把工厂模式用在两个地方,以便根据网络条件创建专门的请求对象。在创建XHR对象时已经用过了简单工厂模式。另一个工厂则用来返回各种处理器类,它们都派生自SimpleHandler。 
356  首先要做的是创建两个新的处理器类。QueuedHandler会在发起新的请求之前先确保所有请求都已经成功处理。而OfflineHandler则会在用户处于离线状态时把请求缓存起来。 
357  */ 
358 /* QueueHandler class */ 
359 var QueuedHandler = function () {       // implements AjaxHandler 
360     this.queue = [];        // 保存请求队列 
361     this.requestInProgress = false;     // 请求进程,是否完成 
362     this.retryDelay = 5;        // In seconds   延时(秒) 
363 }; 
364 extend(QueuedHandler, SimpleHandler); 
365 /** 
366  * 
367  * @param {Boolean} override 是否需要覆盖请求 
368  */ 
369 QueuedHandler.prototype.request = function (method, url, callback, postVars, override) { 
370     if (this.requestInProgress && !override) { 
371         // 其它请求正在进行中且不覆盖请求 
372         this.queue.push({ 
373             method: method, 
374             url: url, 
375             callback: callback, 
376             postVars: postVars 
377         }); 
378     } else { 
379         // 当前没有请求进行,立刻执行该请求 
380         // 执行成功后递归 
381         this.requestInProgress = true; 
382         var xhr = this.createXhrObject(); 
383         var that = this; 
384         xhr.onreadystatechange = function () { 
385             if (xhr.readyState !== 4) { 
386                 return; 
387             } 
388             if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { 
389                 // 该请求成功,执行对应队列的成功方法 
390                 callback.success(xhr.responseText, xhr.responseXML); 
391                 // 处理位于最前队列的请求 
392                 that.advanceQueue(); 
393             } else { 
394                 callback.failure(xhr.status); 
395                 setTimeout(function () { 
396                     that.request(method, url, callback, postVars, true); 
397                 }, that.retryDelay * 1000); 
398             } 
399         }; 
400         xhr.open(method, url, true); 
401         if (method !== 'POST') { 
402             postVars = null; 
403         } 
404         xhr.send(postVars); 
405     } 
406 }; 
407  
408 QueuedHandler.prototype.advanceQueue = function () { 
409     if (this.queue.length === 0) { 
410         this.requestInProgress = false; 
411         return; 
412     } 
413     var req = this.queue.shift(); 
414     this.request(req.method, req.url, req.callback, req.postVars, true); 
415 }; 
416  
417 /* 
418  QueueHandler的request方法与SimpleHandler的看上去差不多,但在允许发起新请求之前它会先检查一下,以确保当前没有别的请求正在处理。如果有哪个请求未能成功处理,那么它还会在指定的时间间隔之后再次重复这个请求,直到该请求被成功处理为止。 
419  */ 
420  
421 /* OfflineHandler class */ 
422 var OfflineHandler = function () {      // implements AjaxHandler 
423     this.storedRequests = []; 
424 }; 
425 extend(OfflineHandler, SimpleHandler); 
426 OfflineHandler.prototype.request = function (method, url, callback, postVars) { 
427     if (XhrManager.isOffline()) { 
428         // Store the requests until we are online 
429         this.storedRequests.push({ 
430             method: method, 
431             url: url, 
432             callback: callback, 
433             postVars: postVars 
434         }); 
435     } else { 
436         // Call SimpleHandler's request method if we are online 
437         this.flushStoredRequests(); 
438         OfflineHandler.superclass.request(method, url, callback, postVars); 
439     } 
440 }; 
441 OfflineHandler.prototype.flushStoredRequests = function () { 
442     for (var i = 0, len = this.storedRequests.length; i < len; i++) { 
443         var req = this.storedRequests[i]; 
444         OfflineHandler.superclass.request(req.method, req.url, req.callback, req.postVars); 
445     } 
446 }; 
447 /* 
448  XhrManager.isOffline方法的作用在于判断用户时都处于在线状态。OfflineHandler只有在用户处于在线状态时才会使用SimpleHandler的request方法实际发起请求。而且一旦探测到用户处于在线状态,它还会立即执行所有缓存中的请求。 
449  */ 
450  
451 /* XhrManager singleton */ 
452 var XhrManager = { 
453     createXhrHandler: function () { 
454         var xhr; 
455         if (this.isOffline()) { 
456             xhr = new OfflineHandler(); 
457         } else if (this.isHighLatency()) { 
458             xhr = new QueuedHandler(); 
459         } else { 
460             xhr = new SimpleHandler(); 
461         } 
462  
463         Interface.ensureImplements(xhr, AjaxHandler); 
464         return xhr; 
465     }, 
466     test: function () { 
467         this.timer = {}; 
468         this.timeout = 8000; 
469         var that = this; 
470         var myHandler = new SimpleHandler(); 
471         var callback = { 
472             success: function (responseText) { 
473                 window.clearTimeout(that.timer); 
474                 alert('Success:' + responseText); 
475             }, 
476             failure: function (statusCode) { 
477                 alert('Failure:' + statusCode); 
478             } 
479         }; 
480  
481         this.timer = window.setTimeout(that.test, that.timeout); 
482         myHandler.request('GET', 'script.php', callback); 
483     }, 
484     isOffline: function () { 
485         // Do a quick request with SimpleHandler and 
486         // see if it succeeds 
487  
488  
489     }, 
490     isHighLatency: function () { 
491         // Do a series of requests with SimpleHandler and 
492         // time the responses.Best done once, as a 
493         // branching function. 
494     } 
495 }; 
496  
497 var myHandler = XhrManager.createXhrHandler(); 
498 var callback = { 
499     success: function (responseText) { 
500         alert('Success: ' + responseText); 
501     }, 
502     failure: function (statusCode) { 
503         alert('Failure: ' + statusCode); 
504     } 
505 }; 
506 myHandler.request('GET', 'script.php', callback); 
507  
508 /* 
509  createXhrHandler方法返回的各种对象都具有我们所需要的一些方法。而且,因为它们都派生自SimpleHandler,所以createXhrObject这个复杂的方法只需要在这个类中实现一次即可,那些子类可以使用这个方法。OfflineHandler中还有多处使用了SimpleHandler的request方法,这进一步实现了代码的重用。 
510  */ 
511  
512 // 工厂模式之利 
513 /* 
514  工厂模式的主要好处在于消除对象间的耦合。通过使用工厂方法而不是new关键字及具体类。你可以把所有实例化代码集中在一个位置。这可以大大简化更换所用的类或在运行期间动态选择所用的类的工作。在派生子类时它也提供了更大的灵活性。使用工厂模式,你可以创建一个抽象的父类,然后在子类中创建工厂方法,从而把成员对象的实例化推迟到更专门化的子类中进行。 
515  所有这些好处都与面向对象涉及的这两条原则有关:弱化对象间的耦合;防止代码的重用。这些都有助于创建模块化的代码。 
516  */ 
517  
518 // 工厂模式之弊 
519 /* 
520  如果根本不可能另外换用一个类,或者不需要在运行期间在一系列可互换的类中进行选择,那就不应该使用工厂方法。大多数类最好使用new关键字和构造函数公开地进行实例化。这可以让代码更简单易读。你可以一眼就看到调用的是什么构造函数,而不必去查看某个工厂方法以便知道实例化的是什么类。工厂方法在其使用场合非常有用,但切勿滥用。 
521  */ 
522  
523 /* 
524  相关模式 
525  
526  * 简单工厂和抽象工厂模式 
527  * 
528  简单工厂使用来选择实现的,可以选择任意接口的实现。一个简单工厂可以有多个用于选择并创建对象的方法,多个方法创建的对象可以有关系也可以没有关系。 
529  抽象工厂模式使用来选择产品簇的实现的,也就是说一般抽象工厂里面有多个用于选择并创建对象的方法,但是这些方法所创建的对象之间通常是有关系的,这些被创建的对象通常是构成一个产品簇所需要的部件对象。 
530  所以从某种意义上来说,简单工厂和抽象工厂是类似的,如果抽象工厂退化成为只有一个实现,不分层次,那么就相当于简单工厂了。 
531  
532  * 简单工厂和工厂方法模式 
533  * 
534  工厂方法的本质也使用来选择实现的,跟简单工厂的区别在于工厂方法是把选择具体实现的功能延迟到子类去实现。 
535  如果把工厂方法中选择的是先放到父类直接实现,那就等于简单工厂。 
536  
537  *简单工厂和能创建对象实例的模式 
538  * 
539  简单工厂可以跟其他任何能够具体的创建对象实例的模式配合使用,比如:单例模式,原型模式,生成器模式等。 
540  
541  * 工厂方法和模板方法模式 
542  * 
543  这两个模式外观类似,都有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定算法骨架提供某些步骤的实现。 
544  这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建末班方法需要的对象。 
545  */ 
546  
547  
548 /* AjaxHandler interface */ 
549 var AjaxHandler = new Interface('AjaxHandler', ['request', 'createXhrObject']); 
550  
551 /* SimpleHandler class */ 
552 var SimpleHandler = function () { 
553 };     // implements AjaxHandler 
554 SimpleHandler.prototype = { 
555     request: function (method, url, callback, postVars) { 
556         var xhr = this.createXhrObject(); 
557         xhr.onreadystatechange = function () { 
558             if (xhr.readyState !== 4) { 
559                 return; 
560             } 
561             ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ? 
562                 callback.success(xhr.responseText, xhr.responseXML) : 
563                 callback.failure(xhr.status); 
564         }; 
565         xhr.open(method, url, true); 
566         if (method !== 'POST') { 
567             postVars = null; 
568         } 
569         xhr.send(postVars); 
570     }, 
571     createXhrObject: function () {      // Factory method 
572         var methods = [ 
573             function () { 
574                 return new XMLHttpRequest(); 
575             }, 
576             function () { 
577                 return new ActiveXObject('Msxml2.XMLHTTP'); 
578             }, 
579             function () { 
580                 return new ActiveXObject('Microsoft.XMLHTTP'); 
581             } 
582         ]; 
583  
584         for (var i = 0, len = methods.length; i < len; i++) { 
585             try { 
586                 methods[i](); 
587             } catch (e) { 
588                 continue; 
589             } 
590             // if we reach this point,method[i] worked 
591             this.createXhrObject = methods[i];    // Memoize the method 
592             return methods[i](); 
593         } 
594  
595         // if we reach this point,none of the methods worked 
596         throw new Error('SimpleHandler:Could not create an XHR object.'); 
597     } 
598 }; 
599  
600 function $() { 
601     var elements = []; 
602     for (var i = 0; i < arguments.length; i++) { 
603         var element = arguments[i]; 
604         if (typeof element == 'string') { 
605             element = document.getElementById(element); 
606         } 
607         if (arguments.length === 1) { 
608             return element; 
609         } 
610         elements.push(element); 
611     } 
612     return elements; 
613 } 
614  
615 /*------------------ 
616  显示类,它把输出内容包装为一个无序列表 
617  --------------------*/ 
618 /* DisplayModule interface */ 
619 var DisplayModule = new Interface('DisplayModule', ['append', 'remove', 'clear']); 
620  
621 /* ListDisplay class. */ 
622 var ListDisplay = function (id, parent) { 
623     // implements Display 
624     this.list = document.createElement('ul'); 
625     this.list.id = id; 
626     parent.appendChild(this.list); 
627 }; 
628 ListDisplay.prototype = { 
629     append: function (text) { 
630         var newEl = document.createElement('li'); 
631         this.list.appendChild(newEl); 
632         newEl.innerHTML = text; 
633         return newEl; 
634     }, 
635     remove: function (el) { 
636         this.list.removeChild(el); 
637     }, 
638     clear: function () { 
639         this.list.innerHTML = ''; 
640     } 
641 }; 
642  
643 /* 配置对象,它包含着一些供阅读器类及其成员对象使用的设置 */ 
644 /* Configuration */ 
645 var conf = { 
646     id: 'cnn-top-stories', 
647     feedUrl: 'http://rss.cnn.com/rss/cnn_topstories.rss', 
648     updateInterval: 60, // In seconds 
649     parent: $('feed-readers') 
650 }; 
651  
652 /* FeedReader类用XHR处理器从RSS源获取XML格式的数据并用一个内部方法对其进行解析,然后用显示模块将解析出来的信息输出到网页上 */ 
653 /* FeedReader class. */ 
654 var FeedReader = function (display, xhrHandler, conf) { 
655     this.display = display; 
656     this.xhrHandler = xhrHandler; 
657     this.conf = conf; 
658  
659     this.startUpdates(); 
660 }; 
661 FeedReader.prototype = { 
662     fetchFeed: function () { 
663         var that = this; 
664         var callback = { 
665             success: function (text, xml) { 
666                 that.parseFeed(text, xml); 
667             }, 
668             failure: function (status) { 
669                 that.showError(status); 
670             } 
671         }; 
672         this.xhrHandler.request('GET', 'feedProxy.php?feed=' + this.conf.feedUrl, callback); 
673  
674     }, 
675     parseFeed: function (responseText, responseXML) { 
676         this.display.clear(); 
677         var items = responseXML.getElementsByTagName('item'); 
678         for (var i = 0, len = items.length; i < len; i++) { 
679             var title = items[i].getElementsByTagName('title')[0]; 
680             var link = items[i].getElementsByTagName('link')[0]; 
681             this.display.append('<a href="' + link.firstChild.data + '">' + title.firstChild.data + '"</a>'); 
682         } 
683     }, 
684     showError: function (statusCode) { 
685         this.display.clear(); 
686         this.display.append('Error fetching feed.'); 
687     }, 
688     stopUpdates: function () { 
689         clearInterval(this.interval); 
690     }, 
691     startUpdates: function () { 
692         this.fetchFeed(); 
693         var that = this; 
694         this.interval = setInterval(function () { 
695             that.fetchFeed(); 
696         }, this.conf.updateInterval * 1000); 
697     } 
698 }; 
699  
700 /* 把所有类和对象拼装恰来的工厂方法 */ 
701 /* FeedManager namespace */ 
702 var FeedManager = { 
703     createFeedReader: function (conf) { 
704         var displayModule = new ListDisplay(conf.id + '-display', conf.parent); 
705         var simpleHandler = new SimpleHandler(); 
706         var xhrHandler = simpleHandler.createXhrObject(); 
707  
708         return new FeedReader(displayModule, xhrHandler, conf); 
709     } 
710 }; 
711  
712  
713 /* Title: Factory method 
714  Description: creates objects without specifying the exact class to create 
715  */ 
716 var jsp = {}; 
717 jsp.dom = {}; 
718 jsp.dom.Text = function () { 
719     this.insert = function (where) { 
720         var txt = document.createTextNode(this.url); 
721         where.appendChild(txt); 
722     }; 
723 }; 
724 jsp.dom.Link = function () { 
725     this.insert = function (where) { 
726         var link = document.createElement('a'); 
727         link.href = this.url; 
728         link.appendChild(document.createTextNode(this.url)); 
729         where.appendChild(link); 
730     }; 
731 }; 
732 jsp.dom.Image = function () { 
733     this.insert = function (where) { 
734         var im = document.createElement('img'); 
735         im.src = this.url; 
736         where.appendChild(im); 
737     }; 
738 }; 
739 jsp.dom.factory = function (type) { 
740     return new jsp.dom[type]; 
741 } 
742  
743 var o = jsp.dom.factory('Link'); 
744 o.url = 'http://google.com'; 
745 o.insert(document.body); 
746  
747  
748 /* 
749  工厂方法模式与IoC与DI 
750  
751  Ioc --- Inversion of Control, 控制反转 
752  DI  --- Dependency Injection, 依赖注入 
753  
754  1).参与者有谁? 
755  一般有三方参与者,一个是某个对象(任意对象);另一个是Ioc/DI的容器(用来实现IoC/DI功能的一个框架程序);还有一个是某个对象的外部资源(对象需要的,但是是从对象外部获取的,都统称为资源)。 
756  2).谁依赖于谁? 
757  某个对象依赖于IoC/DI的容器 
758  3).为什么需要依赖? 
759  对象需要Ioc/DI的容器来提供对象需要的外部资源。 
760  4).谁注入谁? 
761  Ioc/DI的容器注入某个对象。 
762  5).到底注入什么? 
763  注入的是某个对象所需要的外部资源。 
764  6).谁控制谁? 
765  IoC/DI的容器来控制对象。 
766  7).控制什么? 
767  主要是控制对象实例的创建。 
768  8).为何叫反转? 
769  反转是相对于正向而言的。 
770  在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。反向就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向地注入到A类中。 
771  9).依赖注入和控制反转是同一个概念吗? 
772  依赖注入和控制反转是对同一件事情的不同描述。依赖注入是从应用程序的角度去描述。应用程序依赖容器创建并注入它所需要的外部资源。 
773  而控制反转是从容器的角度去描述,容器控制应用程序,由容器反向地向应用程序注入其所需要的外部资源。 
774  
775  这么一个小小的改变其实是编程思想的一个大进步,有效地分离了对象和它所需要的外部资源,使它们松散耦合,有利于功能服用,更重要的是使得程序的整个体系结构变得非常灵活。 
776  
777  
778  */ 
779  
780 // 用setter注入,使用Ioc/DI的示例 
781 var A = function () { 
782     this.c = null; 
783 }; 
784 A.prototype = { 
785     setC: function (c) { 
786         // 注入 
787         this.c = c; 
788     }, 
789     t1: function () { 
790         // 等待注入 
791         this.c.tc(); 
792     } 
793 }; 
794  
795 var C = function () { 
796 }; 
797 C.prototype.tc = function () { 
798     console.log('instance C method'); 
799 }; 
800  
801 (new A()).setC(new C()).t1(); 
802  
803 // 工厂方法实现Ioc/DI 
804 var A = function () { 
805 }; 
806 A.prototype = { 
807     createC1: function () { 
808     }, 
809     t1: function () { 
810         this.createC1().tc(); 
811     } 
812 }; 
813 // 子类实现注入 
814 var A2 = function () { 
815     this.superclass.constructor.apply(this, arguments); 
816 }; 
817 extend(A, A2); 
818 A2.prototype.createC1 = function () { 
819     return new C(); 
820 }; 
821  
822  
823 // http://www.dofactory.com/javascript-factory-method-pattern.aspx 
824  
825 (function () { 
826     function Factory() { 
827         this.createEmployee = function (type) { 
828             var employee; 
829  
830             if (type === "fulltime") { 
831                 employee = new FullTime(); 
832             } else if (type === "parttime") { 
833                 employee = new PartTime(); 
834             } else if (type === "temporary") { 
835                 employee = new Temporary(); 
836             } else if (type === "contractor") { 
837                 employee = new Contractor(); 
838             } 
839  
840             employee.type = type; 
841             employee.say = function () { 
842                 log.add(this.type + ": rate " + this.hourly + "/hour"); 
843             } 
844             return employee; 
845         } 
846  
847     } 
848  
849     var FullTime = function () { 
850         this.hourly = "$12"; 
851     }; 
852     var PartTime = function () { 
853         this.hourly = "$11"; 
854     }; 
855     var Temporary = function () { 
856         this.hourly = "$10"; 
857     }; 
858     var Contractor = function () { 
859         this.hourly = "$15"; 
860     }; 
861  
862 // log helper 
863     var log = (function () { 
864         var log = ""; 
865         return { 
866             add: function (msg) { 
867                 log += msg + "\n"; 
868             }, 
869             show: function () { 
870                 alert(log); 
871                 log = ""; 
872             } 
873         } 
874     })(); 
875  
876  
877     function run() { 
878  
879         var employees = []; 
880  
881         var factory = new Factory(); 
882  
883         employees.push(factory.createEmployee("fulltime")); 
884         employees.push(factory.createEmployee("parttime")); 
885         employees.push(factory.createEmployee("temporary")); 
886         employees.push(factory.createEmployee("contractor")); 
887  
888         for (var i = 0, len = employees.length; i < len; i++) { 
889             employees[i].say(); 
890         } 
891  
892         log.show(); 
893     } 
894 }()); 
895  
896  
897 </script> 
898 </body> 
899 </html>

本文参考链接:https://www.cnblogs.com/webFrontDev/archive/2013/02/01/2888564.html