• javascript设计模式组合模式


      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>
  • 相关阅读:
    Delphi 日期函数列表
    Delphi Copy 函数 和 Pos函数
    delphi xe10 手机程序事件服务操作、退出键操作
    delphi xe10 安卓设备信息
    delphi xe10 获取屏幕截图
    Battery electric vehicles (BEVs) 快充技术
    短波红外(SWIR)相机camera
    多核片上系统(SoC)架构的嵌入式DSP软件设计
    工业4.0是个白日梦吗?
    电子设计搜索引擎引入分析和见解
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/2978510.html
Copyright © 2020-2023  润新知