1 <!DOCTYPE HTML>
  2 <html lang="en-US">
  3 <head>
  4     <meta charset="utf-8">
  5     <title></title>
  6 </head>
  7 <body>
  8 <script>
  9 /**
 10  * 组合模式
 11  *
 12  * 定义:
 13  * 将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
 14  *
 15  * 本质:
 16  * 统一叶对象和组合对象
 17  *
 18  * 组合模式是一种专为创建web上的动态用户界面而量身定制的模式。使用这种模式,可以用一条命命令在多个对象上激发复杂的或递归行为。这可以简化粘合性代码,使其更容易维护,而那些复杂行为则被委托给各个对象。
 19  * 组合模式带来的好处
 20  * (1),你可以用同样的方法处理对象的集合与其中的特定子对象。组合对象(composite)与组成它的对象实现了同一批操作。对组合对象执行的这些操作将向下传递到所有的组成对象(constituent object),这样一来所有的组成对象都会执行同样的操作。在存在大批对象的情况下,这是一种非常有效的技术。藉此可以不着痕迹地用一组对象替换一个对象,反之亦然,这有助于弱化各个对象之间的耦合。
 21  * (2),它可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历。所有组合对象都实现了一个用来获取其子对象的方法。借助这个方法,你可以隐藏实现的细节并随心所欲地组织子对象,任何使用这个对象的代码都不会对其内部实现形成依赖。
 22  *
 23  * 目的:
 24  * 让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。
 25  *
 26  * 对象树:
 27  * 组合模式会组合出树型结构,组成这个树型结构所使用的多个组件对象,就自然的形成了对象树。
 28  *
 29  * 组合模式中的递归
 30  * 组合模式中的递归,指的是对象递归组合,不是常说的递归算法。在设计上称作递归关联,是对象关联关系中的一种。
 31  *
 32  * 透明性的实现
 33  * 如果把管理子组件的操作定义在Component中,那么客户端只需要面对Component,而无需关心具体的组件类型,这种实现方式就是透明性的实现。
 34  * 但是透明性的实现是以安全性为代价的,因为在Component中定义的一些方法,对于叶子对象来说是没有意义的。
 35  * 组合模式的透明性实现,通常的方式是:在Component中声明管理子组件的操作,并在Component中为这些方法提供默认的实现,如果子对象不支持的功能,默认的实现可以是抛出一个例外,来表示不支持这个功能。
 36  *
 37  * 安全性实现
 38  * 如果把管理子组件的操作定义在Composite中,那么客户在使用叶子对象的时候,就不会发生使用添加子组件或是删除子组件的操作了,因为压根就没有这样的功能,这种实现方式是安全的。
 39  * 但是这样就必须区分Composite对象还是叶子对象,对客户而言这是不透明的。
 40  *
 41  * 两种各种方式的选择
 42  * 对于组合模式而言,会更看重透明性,毕竟组合模式的功能就是要让用户对叶子对象和组合对象的使用具有一致性。
 43  *
 44  *
 45  *
 46  */
 47
 48 /*
 49  组合对象的结构
 50  在组合对象的层次体系中有两种类型的对象叶对象和组合对象。这是一个递归定义,但这正是组合模式如此有用的原因所在。一个组合对象由一些别的组合对象和叶对象组成。其中只有叶对象不再包含子对象。叶对象是组合对象中最基本的元素,也是各个操作的落实地点。
 51  */
 52
 53 /*
 54  使用组合模式
 55  只有同时具备吐下两个条件时才适合使用组合模式:
 56  1.存在一批组织成某种层次体系的对象(具体的结构在开发期间可能无法得知)。
 57  2.希望对这批对象和其中的一部分对象实施一个操作。
 58
 59  组合模式擅长于对大批对象进行操作。它专为组织这类对象并把操作从一个层次向下一层次传递而设计。藉此可以弱化对相见的耦合并可互换地使用一些类或示例。按这种模式编写的代码模块化程度更高,也更容易维护。
 60  */
 61
 62 (function () {
 63     function Component() {}
 64
 65     Component.prototype = {
 66         someOperation: function () {},
 67         addChild: function () {
 68             throw new Error('object doesn\'t support this method: addChild');
 69         },
 70         removeChild: function () {
 71             throw new Error('object doesn\'t support this method: removeChild');
 72         },
 73         getChild: function () {
 74             throw new Error('object doesn\'t support this method: getChild');
 75         }
 76     };
 77
 78     // 组合对象,通常需要存储子对象,定义有子部件的部件行为
 79     function Composite() {
 80         this.childComponents = [];
 81     }
 82
 83     Composite.prototype.__proto__ = Component.prototype;
 84     Composite.prototype.someOperation = function () {
 85         for (var i = 0, len = this.childComponents.length; i < len; i++) {
 86             this.childComponents.someOperation();
 87         }
 88     };
 89     Composite.prototype.addChild = function (child) {
 90         this.childComponents.push(child);
 91     };
 92     Composite.prototype.removeChild = function (child) {
 93         var childComponent;
 94         for (var i = 0, len = this.childComponents.length; i < len; i++) {
 95             childComponent = this.childComponents[i];
 96
 97             if (childComponent == child) return true;
 98         }
 99
100         return false;
101     };
102     Composite.prototype.getChildren = function (index) {
103         if (index >= 0 && index < this.childComponents.length) {
104             return this.childComponents[index];
105         }
106         return null;
107     };
108
109     // 叶子对象,也子对象不再包含其他子对象
110     function Leaf() {}
111
112     Leaf.prototype.__proto__ = Component.prototype;
113     Leaf.prototype.someOperation = function () {};
114
115     var root = new Composite();
116     var a = new Composite();
117     var b = new Composite();
118
119     var leaf1 = new Leaf();
120     var leaf2 = new Leaf();
121     var leaf3 = new Leaf();
122
123     root.addChild(a);
124     root.addChild(b);
125     root.addChild(leaf1);
126     a.addChild(leaf2);
127     b.addChild(leaf3);
128
129     var o = root.getChildren(1);
130     console.log(o);
131 }());
132
133 (function () {
134     // 父组件引用
135
136     function Component() {
137         this.parent = null;
138     }
139
140     Component.prototype = {
141         getChildren: function () {
142             throw new Error('object doesn\'t support this method');
143         },
144         addChild: function () {
145             throw new Error('object doesn\'t support this method: addChild');
146         },
147         removeChild: function () {
148             throw new Error('object doesn\'t support this method: removeChild');
149         },
150         getChild: function () {
151             throw new Error('object doesn\'t support this method: getChild');
152         },
153         printStruct: function () {
154             throw new Error('object doesn\'t support this method');
155         }
156     };
157
158     function Composite(name) {
159         this.childComponents = [];
160         this.name = name;
161     }
162
163     Composite.prototype.__proto__ = Component.prototype;
164     Composite.prototype.addChild = function (child) {
165         this.childComponents.push(child);
166
167         child.parent = this;
168     };
169     Composite.prototype.removeChild = function (child) {
170         var idx = this.childComponents.indexOf(child);
171
172         if (idx !== -1) {
173             for (var i = 0, len = child.getChildren().length; i < len; i++) {
174                 var c = child.getChildren()[i];
175                 c.parent = this;
176                 this.childComponents.push(c);
177             }
178
179             this.childComponents.splice(idx, 1);
180         }
181     };
182     Composite.prototype.getChildren = function () {
183         return this.childComponents;
184     };
185     Composite.prototype.printStruct = function (preStr) {
186         preStr = preStr || '';
187         console.log(preStr + '+' + this.name);
188         preStr += '  ';
189         for (var i = 0, len = this.childComponents.length; i < len; i++) {
190             var c = this.childComponents[i];
191             c.printStruct(preStr);
192         }
193     };
194
195     function Leaf(name) {
196         this.name = name;
197     }
198
199     Leaf.prototype.__proto__ = Component.prototype;
200     Leaf.prototype.printStruct = function (preStr) {
201         preStr = preStr || '';
202         console.log(preStr + '-' + this.name);
203     };
204
205     var root = new Composite('服装');
206     var c1 = new Composite('男装');
207     var c2 = new Composite('女装');
208
209     var leaf1 = new Leaf('衬衣');
210     var leaf2 = new Leaf('夹克');
211     var leaf3 = new Leaf('裙子');
212     var leaf4 = new Leaf('套装');
213
214     root.addChild(c1);
215     root.addChild(c2);
216     c1.addChild(leaf1);
217     c1.addChild(leaf2);
218     c2.addChild(leaf3);
219     c2.addChild(leaf4);
220
221     root.printStruct();
222     console.log('-----------------------------');
223
224     root.removeChild(c1);
225     root.printStruct();
226 }());
227
228
229 (function () {
230     // 环状引用
231
232     // 应该要检测并避免出现环状引用,否则容易引起死循环,或是同一个功能被操作多次。
233
234     function Component() {
235         this.componentPath = '';
236     }
237
238     Component.prototype = {
239         printStruct: function (preStr) {},
240         getChildren: function () {
241             throw new Error('object doesn\'t support this method');
242         },
243         addChild: function () {
244             throw new Error('object doesn\'t support this method: addChild');
245         },
246         removeChild: function () {
247             throw new Error('object doesn\'t support this method: removeChild');
248         },
249     };
250
251     function Composite(name) {
252         this.name = name;
253         this.childComponents = [];
254     }
255
256     Composite.prototype.__proto__ = Component.prototype;
257     Composite.prototype.addChild = function (child) {
258         this.childComponents.push(child);
259
260         if (!this.componentPath || !this.componentPath.trim().length) {
261             this.componentPath = this.name;
262         }
263
264         if (this.componentPath.startsWith(child.name + '.')) {
265             throw new Error('该组件' + chid.name + ' 已被添加过了');
266         } else {
267             if (this.componentPath.indexOf('.' + child.name) < 0) {
268                 child.componentPath = this.componentPath + '.' + child.name;
269             } else {
270                 throw new Error('该组件' + child.name + ' 已被添加过了');
271             }
272         }
273     };
274     Composite.prototype.printStruct = function (preStr) {
275         console.log(preStr + '+' + this.name);
276
277         for (var i = 0, len = this.childComponents.length; i < len; i++) {
278             var c = this.childComponents[i];
279             c.printStruct(preStr);
280         }
281     };
282
283     function Leaf(name) {
284         this.name = name;
285     }
286
287     Leaf.prototype.__proto__ = Component.prototype;
288     Leaf.prototype.printStruct = function (preStr) {
289         preStr = preStr || '';
290         console.log(preStr + '-' + this.name);
291     };
292
293     var root = new Composite('服装');
294     var c1 = new Composite('男装');
295     var c2 = new Composite('衬衣');
296
297     root.addChild(c1);
298     c1.addChild(c2);
299     c2.addChild(c1);
300
301     root.printStruct();
302
303     /*
304     当某个组件被删除后,路径发生变化,需要修改所有相关路径记录情况。
305     更好的方式是,使用动态计算路径,每次添加一个组件的时候,动态地递归寻找父组件,然后父组件再找父组件,直到根组件。
306     */
307 }());
308
309
310 // CompositeForm类
311 var CompositeForm = function (id, method, action) {
312     // implements Composite, FormItem
313     this.formComponents = [];
314
315     this.element = document.createElement('form');
316     this.element.id = id;
317     this.element.method = method || 'POST';
318     this.element.action = action || '#';
319 };
320
321 CompositeForm.prototype.add = function (child) {
322     this.formComponents.push(child);
323     this.element.appendChild(child.getElement());
324 };
325 CompositeForm.prototype.remove = function (child) {
326     for (var i = 0, len = this.formComponents.length; i < len; i++) {
327         if (this.formComponents[i] === child) {
328             this.formComponents.splice(i, 1);
329             break;
330         }
331     }
332 };
333 CompositeForm.prototype.getChild = function (i) {
334     return this.formComponents[i];
335 };
336 CompositeForm.prototype.save = function () {
337     for (var i = 0, len = this.formComponents.length; i < len; i++) {
338         this.formComponents[i].save();
339     }
340 };
341 CompositeForm.prototype.getElement = function () {
342     return this.element;
343 };
344 CompositeForm.prototype.restore = function () {
345     for (var i = 0, len = this.formComponents.length; i < len; i++) {
346         this.formComponents[i].restore();
347     }
348 };
349
350
351 // Field叶对象类
352 var Field = function (id) {
353     // implements Composite, FormItem
354     this.id = id;
355     this.element = document.getElementById(id);
356 };
357 Field.prototype.add = function () {
358 };
359 Field.prototype.remove = function () {
360 };
361 Field.prototype.getChild = function () {
362 };
363 Field.prototype.save = function () {
364     setCookie(this.id, this.getValue());
365 };
366 Field.prototype.getElement = function () {
367     return this.element;
368 };
369 Field.prototype.getValue = function () {
370     throw new Error('Unsupported operation on the class Field');
371 };
372 Field.prototype.restore = function () {
373     this.element.value = getCookie(this.id);
374 };
375
376
377 // InputField叶对象类
378 var InputField = function (id, label) {
379     // implements Composite, FormItem
380     Field.call(this, id);
381
382     this.input = document.createElement('input');
383     this.input.id = id;
384     this.input.type = "text";
385     this.label = document.createElement('label');
386     this.label.setAttribute('for', id);
387     var labelTextNode = document.createTextNode(label);
388     this.label.appendChild(labelTextNode);
389
390     this.element = document.createElement('div');
391     this.element.className = 'input-field';
392     this.element.appendChild(this.label);
393     this.element.appendChild(this.input);
394 };
395
396 // Inherit from Field
397 InputField.prototype.__proto__ = Field.prototype;
398
399 InputField.prototype.getValue = function () {
400     return this.input.value;
401 };
402
403
404 var TextareaField = function (id, label) {
405     // implements Composite, FormItem
406     Field.call(this, id);
407
408     this.textarea = document.createElement('textarea');
409     this.textarea.id = id;
410
411     this.label = document.createElement('label');
412     this.label.setAttribute('for', id);
413     var labelTextNode = document.createTextNode(label);
414     this.label.appendChild(labelTextNode);
415
416     this.element = document.createElement('div');
417     this.element.className = 'input-field';
418     this.element.appendChild(this.label);
419     this.element.appendChild(this.textarea);
420 };
421
422 TextareaField.prototype.__proto__ = Field.prototype;
423
424 TextareaField.prototype.getValue = function () {
425     return this.textarea.value;
426 };
427
428
429 var SelectField = function (id, label, options) {
430     Field.call(this, id);
431
432     this.select = document.createElement('select');
433     this.select.id = id;
434     if (typeof options === 'object') {
435         for (var prop in options) {
436             if (!options.hasOwnProperty(prop)) {
437                 continue;
438             }
439             var newOption = new Option(prop, options[prop]);
440             this.select.add(newOption, undefined);
441         }
442     }
443
444     this.label = document.createElement('label');
445     this.label.setAttribute('for', id);
446     var labelTextNode = document.createTextNode(label);
447     this.label.appendChild(labelTextNode);
448
449     this.element = document.createElement('div');
450     this.element.className = 'input-field';
451     this.element.appendChild(this.label);
452     this.element.appendChild(this.select);
453 };
454 SelectField.prototype.__proto__ = Field.prototype;
455 SelectField.prototype.getValue = function () {
456     return this.select.options[this.select.selectedIndex].value;
457 };
458
459
460 // 汇合起来
461 var contactForm = new CompositeForm('contact-form', 'POST', 'contact.php');
462 contactForm.add(new InputField('first-name', 'First Name:'));
463 contactForm.add(new InputField('last-name', 'Last Name:'));
464 contactForm.add(new InputField('address', 'Address:'));
465 contactForm.add(new InputField('city', 'City:'));
466 stateArray = {
467     'GD': 'guangdong',
468     'HN': 'hunan',
469     'BJ': 'beijing'
470 };
471 contactForm.add(new SelectField('state', 'State:', stateArray));
472 contactForm.add(new InputField('zip', 'Zip:'));
473 contactForm.add(new TextareaField('comments', 'Comments:'));
474
475 document.body.appendChild(contactForm.getElement());
476 addEvent(window, 'unload', function () {
477     contactForm.save();
478 });
479
480 addEvent(window, 'load', function () {
481     contactForm.restore();
482 });
483
484
485 // 向层次体系中添加类
486 var CompositeFieldset = function (id, legendText) {
487     this.components = {};
488
489     this.element = document.createElement('fieldset');
490     this.element.id = id;
491
492     if (legendText) {
493         this.legend = document.createElement('legend');
494         this.legend.appendChild(document.createTextNode(legendText));
495         this.element.appendChild(this.legend);
496     }
497 };
498
499 CompositeFieldset.prototype.add = function (child) {
500     this.components[child.getElement().id] = child;
501     this.element.appendChild(child.getElement());
502 };
503
504 CompositeFieldset.prototype.remove = function (child) {
505     delete this.components[child.getElement().id];
506 };
507
508 CompositeFieldset.prototype.getChild = function (id) {
509     if (this.components[id] !== undefined) {
510         return this.components[id];
511     } else {
512         return null;
513     }
514 };
515
516 CompositeFieldset.prototype.save = function () {
517     for (var id in this.components) {
518         if (!this.components.hasOwnProperty(id)) {
519             continue;
520         }
521         this.components[id].save();
522     }
523 };
524
525 CompositeFieldset.prototype.restore = function () {
526     for (var id in this.components) {
527         if (!this.components.hasOwnProperty(id)) {
528             continue;
529         }
530         this.components[id].restore();
531     }
532 };
533
534 CompositeFieldset.prototype.getElement = function () {
535     return this.element;
536 };
537
538 var contactForm2 = new CompositeForm('contact-form2', 'POST', '#');
539
540 var nameFieldset = new CompositeFieldset('name-fieldset');
541 nameFieldset.add(new InputField('first-name2', 'First Name:'));
542 nameFieldset.add(new InputField('last-name2', 'Last Name'));
543 contactForm2.add(nameFieldset);
544
545 var addressFieldset = new CompositeFieldset('address-fieldset');
546 addressFieldset.add(new InputField('address2', 'Address:'));
547 addressFieldset.add(new InputField('city2', 'City:'));
548 addressFieldset.add(new SelectField('state2', 'State:', stateArray));
549 addressFieldset.add(new InputField('zip2', 'Zip:'));
550 contactForm2.add(addressFieldset);
551 contactForm2.add(new TextareaField('comments2', 'Comments:'));
552 document.body.appendChild(contactForm2.getElement());
553
554 addEvent(window, 'unload', function () {
555     contactForm2.save();
556 });
557 addEvent(window, 'load', function () {
558     contactForm2.restore();
559 });
560
561
562 /*
563  添加更多操作
564
565  可以为Field的构造函数增加一个参数,用以表明该域是否必须填写,然后基于这个属性实现一个验证方法。可以修改restore方法,以便在没有保存难过数据的情况下将其值设置为默认值。甚至还可以添加一个submit方法,用Ajax请求把所有的值发送到服务器端。由于使用了组合模式,添加这些操作并不需要知道表单具体是什么样子。
566  */
567
568 // 图片库
569
570 // DynamicGallery class.
571 var DynamicGallery = function (id) {
572     // implements Composite, GalleryItem
573     this.children = [];
574
575     this.element = document.createElement('div');
576     this.element.id = id;
577     this.element.className = 'dynamic-gallery';
578 };
579
580 DynamicGallery.prototype = {
581     // implement the Composite interface
582     add: function (child) {
583         this.children.push(child);
584         this.element.appendChild(child.getElement());
585     },
586     remove: function (child) {
587         for (var node, i = 0; node = this.getChild(i); i++) {
588             if (node === child) {
589                 this.children.splice(i, 1);
590                 break;
591             }
592         }
593         this.element.removeChild(child.getElement());
594     },
595     getChild: function (i) {
596         return this.children[i];
597     },
598     // implement the GalleryItem interface
599     hide: function () {
600         for (var node, i = 0; node = this.getChild(i); i++) {
601             node.hide();
602         }
603         this.element.style.display = 'none';
604     },
605     show: function () {
606         this.element.style.display = 'block';
607         for (var node, i = 0; node = this.getChild(i); i++) {
608             node.show();
609         }
610     },
611     // Helper methods
612     getElement: function () {
613         return this.element;
614     }
615 };
616
617 /*
618  你也许很想用DOM自身作为保存子元素的数据结构。它已经拥有appendChild和removeChild方法,还有childNodes属性面对与存储和获取组合对象的子对象来说这原本非常理想。问题在于这种做法要求每个相关DOM节点都要具有一个反指其包装对象的引用,以便实现所要求的操作。而在某些浏览器中这会导致内存泄漏。一般来说,最好避免让DOM对象反过来引用JS对象。
619  */
620
621 // GalleryImage class.
622 var GalleryImage = function (src) {
623     // implements Composite, GalleryItem
624     this.element = document.createElement('img');
625     this.element.className = 'gallery-image';
626     this.element.src = src;
627 };
628
629 GalleryImage.prototype = {
630     // implements the Composite interface
631     /*
632      this is a leaf node, so we don't
633      implements these methods,we just
634      define them
635      */
636     add: function () {
637     },
638     remove: function () {
639     },
640     getChild: function () {
641     },
642     // implements the GalleryItem interface
643     hide: function () {
644         this.element.style.display = 'none';
645     },
646     show: function () {
647         // restore the display attribute to
648         // its previus setting.
649         this.element.style.display = '';
650     },
651     // Helper methods
652     getElement: function () {
653         return this.element;
654     }
655 };
656
657 var topGallery = new DynamicGallery('top-gallery');
658
659 topGallery.add(new GalleryImage('img/image-1.jpg'));
660 topGallery.add(new GalleryImage('img/image-2.jpg'));
661 topGallery.add(new GalleryImage('img/image-3.jpg'));
662
663 var vacationPhotos = new DynamicGallery('vacation=photos');
664
665 for (var i = 0; i < 30; i++) {
666     vacationPhotos.add(new GalleryImage('img/image-' + i + '.jpg'));
667 }
668
669 topGallery.add(vacationPhotos);
670 topGallery.show();
671 vacationPhotos.hide();
672 document.body.appendChild(topGallery.getElement());
673
674
675 /*
676  组合模式之利
677 1.定义了包含基本对象和组合对象的类层次结构。
678 在组合模式中,基本对象可以被组合成复杂的组合对象,而组合对象又可以组合成更复杂的组合对象,可以不断地递归组合下去,从而构成一个统一的组合对象的类层次结构。
679
680 2.同意了组合对象和叶子对象
681 在组合模式中,可以把叶子对象当作特殊的组合对象看待,为它们定义统一的父类,从而把组合对象和叶子对象的行为统一起来。
682
683 3.简化了客户端调用
684
685 4.更容易扩展
686 由于客户端是统一地面对Component来操作,因此,新定义的Composite和Leaf子类能够很容易地与已有的结构一起工作,而客户端不需要为增添了新的组件类而改变。
687
688
689  组合模式之弊
690  1.很难限制组合中的组件类型
691  这使得我们必须动态检测组件类型。
692
693
694 何时选用?
695 1.如果你想表示对象的部分--整体层次结构。
696 2.如果你希望统一地使用组合结构中的所有对象。
697
698
699 相关模式
700
701 组合模式与装饰模式
702 可以组合使用。
703 装饰模式在组装多个装饰器对象的时候,是一个装饰器找下一个装饰器,下一个再找下一个,如此递归下去。其实这种结构也可以使用组合模式来帮助构建,这样一来,装饰器对象就相当于组合模式的Composite对象了。
704 要让两个模式能很好地组合使用,通常会让它们有一个公共的父类。因此装饰器必须支持组合模式需要的一些功能,比如,增加,删除子组件。
705
706 组合模式和享元模式
707 可以组合使用。
708 如果组合模式中出现大量相似的组件对象的话,可以考虑使用享元模式来帮助缓存组件对象,这样可以减少内存占用。
709 使用享元模式也是有条件的,如果组件对象的可变化部分的状态能够从组件对象中分离出来,并且组件对象本身不需要向父组件发送请求的话,就可以采用享元模式。
710
711 组合模式和迭代器模式
712 可以组合使用。
713 使用迭代器模式来遍历组合对象的子对象集合,而无需关心具体存放子对象的聚合结构。
714
715 组合模式和访问者模式
716 可以组合使用。
717 访问者模式能够在不修改原有对象结构的情况下,为对象结构中的对象增添新的功能。访问者模式和组合模式合用,可以把原本Composite和Leaf类中的操作和行为都局部化。
718 如果在使用组合模式的时候,预计到以后可能会有增添其他功能的可能,那么可以采用访问者模式,来预留好添加新功能的方式和通道,这样以后再添加新功能的时候,就不需要再修改已有的对象结构和已经实现的功能。
719
720 组合模式和职责链模式
721 可以组合使用。
722 职责链模式要解决的问题是:实现请求的发送者和接收者之间解耦。职责链模式的实现方式是把多个接收者组合起来,构成职责链,然后让请求在这条链上传递,直到有接收者处理这个请求为止。
723 可以应用组合模式来构建这条链,相当于是子组件找父组件,父组件又找父组件,如此递归下去,构成一条处理请求的组件对象链。
724
725 组合模式和命令模式
726 可以组合使用。
727 命令模式中的宏命令就是使用组合模式来组装出来的。
728
729  */
730
731
732 // http://www.dofactory.com/javascript-composite-pattern.aspx
733
734 (function () {
735     var Node = function (name) {
736         this.children = [];
737         this.name = name;
738     }
739
740     Node.prototype = {
741         add: function (child) {
742             this.children.push(child);
743         },
744         remove: function (child) {
745             var length = this.children.length;
746             for (var i = 0; i < length; i++) {
747                 if (this.children[i] === child) {
748                     this.children.splice(i, 1);
749                     return;
750                 }
751             }
752         },
753         getChild: function (i) {
754             return this.children[i];
755         },
756         hasChildren: function () {
757             return this.children.length > 0;
758         }
759     }
760
761     // recursively traverse a (sub)tree
762     function traverse(indent, node) {
763
764         log.add(Array(indent++).join("--") + node.name);
765
766         for (var i = 0, len = node.children.length; i < len; i++) {
767             traverse(indent, node.getChild(i));
768         }
769     }
770
771     // logging helper
772     var log = (function () {
773         var log = "";
774         return {
775             add: function (msg) { log += msg + "\n"; },
776             show: function () {
777                 alert(log);
778                 log = "";
779             }
780         }
781     })();
782
783
784     function run() {
785
786         var tree = new Node("root");
787         var left = new Node("left")
788         var right = new Node("right");
789         var leftleft = new Node("leftleft");
790         var leftright = new Node("leftright");
791         var rightleft = new Node("rightleft");
792         var rightright = new Node("rightright");
793
794         tree.add(left);
795         tree.add(right);
796         tree.remove(right);  // note: remove
797         tree.add(right);
798         left.add(leftleft);
799         left.add(leftright);
800         right.add(rightleft);
801         right.add(rightright);
802
803         traverse(1, tree);
804
805         log.show();
806     }
807 }());
808
809
810 </script>
811 </body>
812 </html>

转载于:https://www.cnblogs.com/webFrontDev/archive/2013/03/24/2978510.html

javascript设计模式-组合模式相关推荐

  1. JavaScript设计模式——组合模式

    CSDN话题挑战赛第2期 参赛话题:学习笔记 学习之路,长路漫漫,写学习笔记的过程就是把知识讲给自己听的过程.这个过程中,我们去记录思考的过程,便于日后复习,梳理自己的思路.学习之乐,独乐乐,不如众乐 ...

  2. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

  3. 设计模式---组合模式

    设计模式---组合模式 什么是组合模式:Composite? 使用场景 代码示例 组合模式模板 组合模式的安全性和透明性 总结 优缺点: 适用场景: 什么是组合模式:Composite? 计算机的文件 ...

  4. Java设计模式 —— 组合模式(Composite)

    Java设计模式 -- 组合模式(Composite) 定义 Composite,组合模式:将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性. ...

  5. JS设计模式--组合模式

    JS设计模式–组合模式 昨天学习了白贺翔老师的JS组合模式,现在把我学到的分享出来叭O(∩_∩)O,直接看下面代码 <!DOCTYPE html> <html lang=" ...

  6. js设计模式——组合模式

    组合模式将对象组合成树形结构,以表示"部分-整体"的层次结构.除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,是的用户对单个对象和组合对象的使用具有一致性. ...

  7. 设计模式----组合模式UML和实现代码

    2019独角兽企业重金招聘Python工程师标准>>> 一.什么是组合模式? 组合模式(Composite)定义:将对象组合成树形结构以表示'部分---整体'的层次结构.组合模式使得 ...

  8. js设计模式-组合模式

    组合模式是一种专为创建web上的动态用户界面而量身定制的模式.使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更容易维护,而那些复杂行为则被委托给各个对象. ...

  9. 大话设计模式—组合模式

    组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这种类型的设计模式属于结构型模式, ...

最新文章

  1. MyBatis之传入参数——parameterType
  2. 节能信标组:让我们一起来内卷
  3. Linux Shell 截取字符串
  4. 提取图像的边界,用数字标记不同的目标边界
  5. Java 8新特性终极指南
  6. LeetCode 1382. 将二叉搜索树变平衡(中序遍历+二分递归)
  7. Qt工作笔记-对主事件循环的进一步认识
  8. Apache Flink 在 bilibili 的多元化探索与实践
  9. vc mysql ado blob_在VC下采用ADO实现BLOB(Binary)数据的存储,读取,修改,删除。...
  10. LaTeX使用--基本语法
  11. com组件调用regsvr32的时候调试DllRegisterServer时候遇到的问题
  12. Python数据去重
  13. Detectron2安装教程
  14. 《创业时代》原型:为什么Talkbox、子弹短信都失败了?
  15. Kafka:增加Topic的分区数
  16. 2022 第二届中国移动“梧桐杯”大数据应用创新大赛-基于移动大数据的网约车司机识别 线上0.95+ 方案
  17. 主体阶段钢筋工程、模板工程、混凝土、管线预埋施工要点都有哪些?
  18. 2023,本命年向阳而生
  19. 基于html5的音乐网站开题,开题报告——基于HTML5的音乐网站.doc
  20. win10下基于anaconda利用keras开展16系显卡GTX1650的GPU神经网络计算

热门文章

  1. 2019.03.13 ZJOI2019模拟赛 解题报告
  2. 使用Nginx实现服务器反向代理和负载均衡
  3. 2018-2019-1 20189204《Linux内核原理与分析》第三周作业
  4. python_104_面向对象总结
  5. .NET 网站自动登录
  6. WebView你真的熟悉吗?看了才知道
  7. int * const a, 和 const int* a,const PInt a;
  8. 面试---如何在List<Integer>中如何存放String类型的数据?
  9. android Binary XML file line #1: Binary XML file line #1: Error inflating class x 问题详解
  10. integer是值传递还是引用传递_数据值Value传递-高位传递