设计模式(10)[JS版]-JavaScript如何实现组合模式???

目录

1 什么是组合模式

2 主要参与者

3 代码实现

4 应用实例

4.1 表单验证

4.1 图片阅读器

5 总结


1 什么是组合模式

组合模式允许创建具有属性的对象,这些对象是原始项目或对象集合。集合中的每个项目本身可以容纳其他集合,创建深度嵌套结构。

树型控件是复合模式的一个完美例子。树的节点要么包含一个单独的对象(叶子节点),要么包含一组对象(节点的子树)。组合模式用于简单化,一致化对单组件和复合组件的使用;其实它就是一棵树。

      组合模式能对于工作能起到简化作用,组合对象实现某一操作时,通过递归,向下传递到所有的组成对象,在存在大批对象时,假如页面的包含许多拥有同样功能的对象,只需要操作组合对象即可达到目标。在存在着某种的层次结构,并且其中的一部分要实现某些操作,即可使用组合模式。

组合模式中的所有节点都共享一组通用的属性和方法,它既支持单个对象,也支持对象集合。这种共同的接口极大地促进了递归算法的设计和构建,这种算法可以对复合集合中的每个对象进行迭代。

实例场景:

1 自然界中的各种树,树长在大地人,树头(树根),即是入口点,这棵树头向上生长,即有自己的叶子,又有自己的子树枝,某树枝还有自己的叶子,跟子树枝。

2  操作系统目录结构、公司部门组织架构、国家省市县等,像这么看起来复杂的现象,都可以使用组合模式,即部分-整体模式来操作。

2 主要参与者

 

参与该模式的对象有:

Component :声明组成中对象的接口。

Leaf :代表构图中的叶子对象,一个叶子没有子对象。

Composite :表示组成中的分支(或子树),维护一个子组件的集合。

3 代码实现

在下边的代码中,Node(节点)对象创建了一个树状结构。每个节点都有一个名字和4个方法:add、remove、getChild和hasChildren。这些方法被添加到Node的原型中。这减少了对内存的要求,因为这些方法现在被所有节点共享。Node是完全递归的,不需要单独的Component或Leaf对象。

通过向父节点添加节点来构建一个小型的复合树。一旦完成,我们调用traverse,它将遍历树中的每个节点,并显示其名称和深度(通过缩进显示)。日志函数用来记录和显示结果。


  
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>组合模式:公众号AlbertYang</title>
  6. </head>
  7. <body>
  8. </body>
  9. <script>
  10. var Node = function(name) {
  11. this.children = [];
  12. this.name = name;
  13. }
  14. Node.prototype = {
  15. add: function(child) {
  16. this.children.push(child);
  17. },
  18. remove: function(child) {
  19. var length = this.children.length;
  20. for (var i = 0; i < length; i++) {
  21. if (this.children[i] === child) {
  22. this.children.splice(i, 1);
  23. return;
  24. }
  25. }
  26. },
  27. getChild: function(i) {
  28. return this.children[i];
  29. },
  30. hasChildren: function() {
  31. return this.children.length > 0;
  32. }
  33. }
  34. // 使用递归遍历一个树
  35. function traverse(indent, node) {
  36. log.add(Array(indent++).join("--") + node.name);
  37. for (var i = 0, len = node.children.length; i < len; i++) {
  38. traverse(indent, node.getChild(i));
  39. }
  40. }
  41. // 日志函数记录和打印结果
  42. var log = (function() {
  43. var log = "";
  44. return {
  45. add: function(msg) {
  46. log += msg + "\n";
  47. },
  48. show: function() {
  49. console.info("%c%s", "color:red; font-size:18px", log);
  50. log = "";
  51. }
  52. }
  53. })();
  54. function run() {
  55. var tree = new Node("root");
  56. var left = new Node("left")
  57. var right = new Node("right");
  58. var leftleft = new Node("leftleft");
  59. var leftright = new Node("leftright");
  60. var rightleft = new Node("rightleft");
  61. var rightright = new Node("rightright");
  62. tree.add(left);
  63. tree.add(right);
  64. tree.remove(right); // 删除节点
  65. tree.add(right);
  66. left.add(leftleft);
  67. left.add(leftright);
  68. right.add(rightleft);
  69. right.add(rightright);
  70. traverse(1, tree);
  71. log.show();
  72. }
  73. run();
  74. </script>
  75. </html>

4 应用实例

4.1 表单验证

    演示地址:https://www.albertyy.com/2020/8/Component1.html

    表单验证中,需要做的工作是表单的保存、恢复和验证表单中的值,然而表单的数量是未知数,类型是未知数,只有功能能确定,在这种情况下,使用组合模式无疑最好,通过给每个表单添加功能,然后一个表单对象组合起来,通过操作表单对象即可达到操作表单。


  
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>组合模式实例应用:公众号AlbertYang</title>
  6. </head>
  7. <body>
  8. <input type="button" value="存储" onclick="a()" />
  9. <input type="button" value="取出" onclick="b()" />
  10. </body>
  11. <script type="text/javascript">
  12. //存储的值
  13. var value_content = {};
  14. function setCookie(name, value) {
  15. value_content[name] = value;
  16. }
  17. function getCookie(name) {
  18. return value_content[name];
  19. }
  20. //表单组合对象
  21. var CompositeForm = function(id, method, action) {
  22. this.formComponents = [];
  23. this.element = document.createElement('form');
  24. this.element.id = id;
  25. this.element.method = method || 'POST';
  26. this.element.action = action || '#';
  27. }
  28. CompositeForm.prototype.add = function(child) {
  29. this.formComponents.push(child);
  30. this.element.appendChild(child.getElement());
  31. }
  32. CompositeForm.prototype.remove = function(child) {
  33. for (var i = 0, len = this.formComponents.length; i < len; i++) {
  34. if (child == this.formComponents[i]) {
  35. this.formComponents.splice(i, 1);
  36. break;
  37. }
  38. }
  39. }
  40. CompositeForm.prototype.getChild = function(i) {
  41. return this.formComponents[i];
  42. }
  43. CompositeForm.prototype.save = function() {
  44. for (var i = 0, len = this.formComponents.length; i < len; i++) {
  45. this.formComponents[i].save();
  46. }
  47. }
  48. CompositeForm.prototype.restore = function() {
  49. for (var i = 0, len = this.formComponents.length; i < len; i++) {
  50. this.formComponents[i].restore();
  51. }
  52. }
  53. CompositeForm.prototype.getElement = function() {
  54. return this.element;
  55. }
  56. //接口方法
  57. var Field = function(id) {
  58. this.id = id;
  59. this.element;
  60. this.content;
  61. };
  62. Field.prototype.add = function() {};
  63. Field.prototype.remove = function() {};
  64. Field.prototype.getChild = function() {};
  65. Field.prototype.save = function() {
  66. setCookie(this.id, this.getValue());
  67. };
  68. Field.prototype.getElement = function() {
  69. return this.element;
  70. }
  71. Field.prototype.getValue = function() {
  72. throw new Error('错误');
  73. }
  74. Field.prototype.restore = function() {
  75. this.content.value = getCookie(this.id);
  76. };
  77. //继承方法
  78. function extend(subClass, superClass) {
  79. var F = function() {};
  80. F.prototype = superClass.prototype;
  81. subClass.prototype = new F();
  82. subClass.prototype.constructor = subClass;
  83. subClass.superclass = superClass.prototype;
  84. if (superClass.prototype.constructor == Object.prototype.constructor) {
  85. superClass.prototype.constructor = superClass;
  86. }
  87. }
  88. //输入框
  89. var InputField = function(id, label) {
  90. Field.call(this, id);
  91. this.input = document.createElement('input');
  92. this.content = this.input;
  93. this.label = document.createElement('label');
  94. var labelTextNode = document.createTextNode(label);
  95. this.label.appendChild(labelTextNode);
  96. this.element = document.createElement('div');
  97. this.element.id = id;
  98. this.element.className = 'input-field';
  99. this.element.appendChild(this.label);
  100. this.element.appendChild(this.input);
  101. }
  102. extend(InputField, Field);
  103. InputField.prototype.getValue = function() {
  104. return this.input.value;
  105. };
  106. //文本框
  107. var TextareaField = function(id, label) {
  108. Field.call(this, id);
  109. this.textarea = document.createElement('textarea');
  110. this.content = this.textarea;
  111. this.label = document.createElement('label');
  112. var labelTextNode = document.createTextNode(label);
  113. this.label.appendChild(labelTextNode);
  114. this.element = document.createElement('div');
  115. this.element.id = id;
  116. this.element.className = 'input-field';
  117. this.element.appendChild(this.label);
  118. this.element.appendChild(this.textarea);
  119. };
  120. extend(TextareaField, Field);
  121. TextareaField.prototype.getValue = function() {
  122. return this.textarea.value;
  123. };
  124. //选择框
  125. var SelectField = function(id, label) {
  126. Field.call(this, id);
  127. this.select = document.createElement('select');
  128. this.select.options.add(new Option("sfs", "sfs"));
  129. this.select.options.add(new Option("111", "2222222222")); //这边value会改变
  130. this.content = this.select;
  131. this.label = document.createElement('label');
  132. var labelTextNode = document.createTextNode(label);
  133. this.label.appendChild(labelTextNode);
  134. this.element = document.createElement('div');
  135. this.element.id = id;
  136. this.element.className = 'input-field';
  137. this.element.appendChild(this.label);
  138. this.element.appendChild(this.select);
  139. };
  140. extend(SelectField, Field);
  141. SelectField.prototype.getValue = function() {
  142. return this.select.options[this.select.options.selectedIndex].value;
  143. };
  144. //表单域
  145. var CompositeFieldset = function(id, legendText) {
  146. this.components = {};
  147. this.element = document.createElement('fieldset');
  148. this.element.id = id;
  149. if (legendText) {
  150. this.legend = document.createElement('legend');
  151. this.legend.appendChild(document.createTextNode(legendText));
  152. this.element.appendChild(this.legend);
  153. }
  154. };
  155. CompositeFieldset.prototype.add = function(child) {
  156. this.components[child.getElement().id] = child;
  157. this.element.appendChild(child.getElement());
  158. };
  159. CompositeFieldset.prototype.remove = function(child) {
  160. delete this.components[child.getElement().id];
  161. };
  162. CompositeFieldset.prototype.getChild = function(id) {
  163. if (this.components[id] != undefined) {
  164. return this.components[id];
  165. } else {
  166. return null;
  167. }
  168. };
  169. CompositeFieldset.prototype.save = function() {
  170. for (var id in this.components) {
  171. if (!this.components.hasOwnProperty(id))
  172. continue;
  173. this.components[id].save();
  174. }
  175. };
  176. CompositeFieldset.prototype.restore = function() {
  177. for (var id in this.components) {
  178. if (!this.components.hasOwnProperty(id))
  179. continue;
  180. this.components[id].restore();
  181. }
  182. };
  183. CompositeFieldset.prototype.getElement = function() {
  184. return this.element;
  185. };
  186. //用组合模式汇合起来
  187. var contactForm = new CompositeForm('contact-form', 'POST', 'test');
  188. var nameFieldset = new CompositeFieldset('name-fieldset');
  189. nameFieldset.add(new InputField('first-name', 'First Name'));
  190. nameFieldset.add(new InputField('last-name', 'Last Name'));
  191. contactForm.add(nameFieldset);
  192. var addressFieldset = new CompositeFieldset('address-fieldset');
  193. addressFieldset.add(new InputField('address', 'Address'));
  194. addressFieldset.add(new InputField('city', 'City'));
  195. addressFieldset.add(new SelectField('state', 'State'));
  196. addressFieldset.add(new InputField('zip', 'Zip'));
  197. contactForm.add(addressFieldset);
  198. contactForm.add(new TextareaField('comments', 'Comments'));
  199. document.body.appendChild(contactForm.getElement());
  200. function a() {
  201. contactForm.save();
  202. }
  203. function b() {
  204. contactForm.restore();
  205. }
  206. </script>
  207. </html>

4.1 图片阅读器

  演示地址:https://www.albertyy.com/2020/8/Component2.html

  图片阅读器与表单验证基本一样,通过汇合操作图片。


  
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>组合模式实例应用:公众号AlbertYang</title>
  6. </head>
  7. <body>
  8. <input value="隐藏" type="button" onclick="a()" />
  9. <input value="显示" type="button" onclick="b()" />
  10. </body>
  11. <script type="text/javascript">
  12. //图片库
  13. var DynamicGallery = function(id) {
  14. this.children = [];
  15. this.element = document.createElement('div');
  16. this.element.id = id;
  17. this.element.className = 'dynamic-gallery';
  18. };
  19. DynamicGallery.prototype = {
  20. add: function(child) {
  21. this.children.push(child);
  22. this.element.appendChild(child.getElement());
  23. },
  24. remove: function(child) {
  25. for (var node, i = 0; node = this.getChild(i); i++) {
  26. if (node == child) {
  27. this.children.splice(i, 1);
  28. break;
  29. }
  30. }
  31. this.element.removeChild(chld.getElement());
  32. },
  33. getChild: function(i) {
  34. return this.children[i];
  35. },
  36. hide: function() {
  37. for (var i = 0, node; node = this.getChild(i); i++) {
  38. node.hide();
  39. }
  40. this.element.style.display = 'none';
  41. },
  42. show: function() {
  43. this.element.style.display = 'block';
  44. for (var i = 0, node; node = this.getChild(i); i++) {
  45. node.show();
  46. }
  47. },
  48. getElement: function() {
  49. return this.element;
  50. }
  51. };
  52. //单个图片
  53. var GalleryImage = function(src) {
  54. this.element = document.createElement('img');
  55. this.element.className = 'gallery-image';
  56. this.element.src = src;
  57. };
  58. GalleryImage.prototype = {
  59. add: function() {},
  60. remove: function() {},
  61. getChild: function() {},
  62. hide: function() {
  63. this.element.style.display = 'none';
  64. },
  65. show: function() {
  66. this.element.style.display = '';
  67. },
  68. getElement: function() {
  69. return this.element;
  70. }
  71. };
  72. //汇合起来
  73. var topGallery = new DynamicGallery('top-gallery');
  74. topGallery.add(new GalleryImage('img/1.jpg'));
  75. topGallery.add(new GalleryImage('img/2.jpg'));
  76. topGallery.add(new GalleryImage('img/3.jpg'));
  77. var vacationPhotos = new DynamicGallery('vacation-photos');
  78. for (var p = 0; p < 30; p++) {
  79. vacationPhotos.add(new GalleryImage('img/3.jpg'));
  80. }
  81. topGallery.add(vacationPhotos);
  82. document.body.appendChild(topGallery.getElement());
  83. function a() {
  84. topGallery.hide();
  85. }
  86. function b() {
  87. topGallery.show();
  88. }
  89. </script>
  90. </html>

5 总结

组合模式通过简单的操作就能达到复杂的效果,一个操作通过遍历递归传递这个操作。不过组合模式的弱点也在于此,如果层次过多,则性能将受到影响。

组合模式应用需要符合两个条件,一是产生递归,二是具有相同的动作。

今天的学习就到这里,你可以使用今天学习的技巧来改善一下你曾经的代码,如果想继续提高,欢迎关注我,每天学习进步一点点,就是领先的开始。如果觉得本文对你有帮助的话,欢迎点赞,评论,转发!!!

文章来源: albertyang.blog.csdn.net,作者:Albert Yang,版权归原作者所有,如需转载,请联系作者。

原文链接:albertyang.blog.csdn.net/article/details/107801403

(完)