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