zoukankan      html  css  js  c++  java
  • 《javascript设计模式》笔记之第九章:组合模式

    之前一直都是按照书的结构顺序做总结,觉得好像不是很好,现在试着完全按照自己的理解做总结。例子还是书上的例子。

    一:组合模式的作用
    在web开发中,主要用于创建嵌套的html结点,使得我们方便的把各种结点连接起来,并且提供简易的操作。
     
    二:组合模式的结构
    结构就像我们的文件结构一样讲Composite理解为文件夹,Leaf理解为文件就好理解了。
     
    三:例子一,创建一个组合的表单
    需求:试想着我们想要构建一个表单,但是表单域经常要被产品经理修改,我们怎样才能利用js快速的搭建这个form呢?此外,我们有个功能,就是可以保存用户填写的表单,那么我们一般的做法是手动把一个个表单域保存到cookie中,如何才能通过一个操作就完成所有表单域的保存呢?
     
    利用组合模式实现是这样的:
     
    思路:创建一个组合表单类CompositeForm,这个类有操作表单域的方法add(),和remove(),传入的参数就是对应的表单域类(InputField,TextareaField等),这样我们就可以很方便的组合表单了。对于保存表单域的功能,我们只要给CompositeForm弄一个save()方法就可以了,这个save()方法就遍历它下面的表单域,自动的保存每个表单域。
     
    具体实现:
    步骤一:
    创建接口,保证表单和表单域都有必须的方法
    var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
    var FormItem = new Interface('FormItem', ['save']);
    
    步骤二:
    定义组合表单类CompositeForm
    var CompositeForm = function(id, method, action) { // implements Composite, FormItem
      this.formComponents = [];
    
      this.element = document.createElement('form');
      this.element.id = id;
      this.element.method = method || 'POST';
      this.element.action = action || '#';
    };
    
    CompositeForm.prototype.add = function(child) {
      Interface.ensureImplements(child, Composite, FormItem);
      this.formComponents.push(child);
      this.element.appendChild(child.getElement());
    };
    
    CompositeForm.prototype.remove = function(child) {
      for(var i = 0, len = this.formComponents.length; i < len; i++) {
        if(this.formComponents[i] === child) {
          this.formComponents.splice(i, 1); // Remove one element from the array at 
                                            // position i.
          break;
        }
      }
    };
    
    CompositeForm.prototype.getChild = function(i) {
      return this.formComponents[i];
    };
    
    CompositeForm.prototype.save = function() {
      for(var i = 0, len = this.formComponents.length; i < len; i++) {
        this.formComponents[i].save();
      }
    };
    
    CompositeForm.prototype.getElement = function() { 
      return this.element; 
    };
    
    步骤三:
    创建Field类,用来派生出表单域的类
    var Field = function(id) { // implements Composite, FormItem
      this.id = id;
      this.element;
    };
    
    Field.prototype.add = function() {};
    Field.prototype.remove = function() {};
    Field.prototype.getChild = function() {};
    
    Field.prototype.save = function() {
      setCookie(this.id, this.getValue);
    };
    
    Field.prototype.getElement = function() { 
      return this.element; 
    };
    
    Field.prototype.getValue = function() { 
      throw new Error('Unsupported operation on the class Field.'); 
    };
    
    步骤三
    创建特定的表单域类
    var InputField = function(id, label) { // implements Composite, FormItem
      Field.call(this, id);
    
      this.input = document.createElement('input');
      this.input.id = id;
    
      this.label = document.createElement('label');
      var labelTextNode = document.createTextNode(label);
      this.label.appendChild(labelTextNode);
    
      this.element = document.createElement('div');
      this.element.className = 'input-field';
      this.element.appendChild(this.label);
      this.element.appendChild(this.input);
    };
    extend(InputField, Field); // Inherit from Field.
    
    InputField.prototype.getValue = function() { 
      return this.input.value;
    };
    
    /* TextareaField class. */
    
    var TextareaField = function(id, label) { // implements Composite, FormItem
      Field.call(this, id);
    
      this.textarea = document.createElement('textarea');
      this.textarea.id = id;
    
      this.label = document.createElement('label');
      var labelTextNode = document.createTextNode(label);
      this.label.appendChild(labelTextNode);
    
      this.element = document.createElement('div');
      this.element.className = 'input-field';
      this.element.appendChild(this.label);
      this.element.appendChild(this.textarea);
    };
    extend(TextareaField, Field); // Inherit from Field.
    
    TextareaField.prototype.getValue = function() { 
      return this.textarea.value;
    };
    
    /* SelectField class. */
    
    var SelectField = function(id, label) { // implements Composite, FormItem
      Field.call(this, id);
    
      this.select = document.createElement('select');
      this.select.id = id;
    
      this.label = document.createElement('label');
      var labelTextNode = document.createTextNode(label);
      this.label.appendChild(labelTextNode);
    
      this.element = document.createElement('div');
      this.element.className = 'input-field';
      this.element.appendChild(this.label);
      this.element.appendChild(this.select);
    };
    extend(SelectField, Field); // Inherit from Field.
    
    SelectField.prototype.getValue = function() {
      return this.select.options[this.select.selectedIndex].value;
    };
    
    步骤四:
    使用
    var contactForm = new CompositeForm('contact-form', 'POST', 'contact.php');
    
    contactForm.add(new InputField('first-name', 'First Name'));
    contactForm.add(new InputField('last-name', 'Last Name'));
    contactForm.add(new InputField('address', 'Address'));
    contactForm.add(new InputField('city', 'City'));
    contactForm.add(new SelectField('state', 'State', stateArray)); // var stateArray =
        [{'al', 'Alabama'}, ...]
    contactForm.add(new InputField('zip', 'Zip'));
    contactForm.add(new TextareaField('comments', 'Comments'));
    
    addEvent(window, 'unload', contactForm.save);
    
    点评:注意CompositeForm里面有formComponents这个属性,里面包含的是每一个表单域的对象,这样的话当我们要用到他们的时候就不用操作DOM来获取了。save()这个方法可以看一下,具体做法就是遍历表单的组件,逐一调用它们的save()方法。由于表单的结构要求,表单域不能嵌套form元素,所以这个例子还不能很好的诠释组合模式,最好情况下组合模式的组合程度是很高的。步骤四中的使用方法中,我们可以发现表单的动态创建是很简单的,保存方法的使用也是很简单,只要操作最contactForm这个变量就可以了。
    书上还有一个对这个表单的扩展,这里就不展开了。
    四:例子二,Image gallery
    下面来看一个更好的例子:
    需求:我们要创建一个图片集,图片集里面有图片集和图片,图片集下面又有图片集和图片。
    思路:创建一个DynamicGallery类,这个类有add()、remove()等方法。add()方法可以传入DynamicGallery类和GalleryImage类。
     
    实现:
    步骤一:创建接口,保证上面提到的两个类符合要求
    var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
    var GalleryItem = new Interface('GalleryItem', ['hide', 'show']);
    
    步骤二:创建DynamicGallery类
    var DynamicGallery = function(id) { // implements Composite, GalleryItem
      this.children = [];
    
      this.element = document.createElement('div');
      this.element.id = id;
      this.element.className = 'dynamic-gallery';
    }
    
    DynamicGallery.prototype = {
    
      // Implement the Composite interface.
    
      add: function(child) {
        Interface.ensureImplements(child, Composite, GalleryItem);
        this.children.push(child);
        this.element.appendChild(child.getElement());
      },
      remove: function(child) {
        for(var node, i = 0; node = this.getChild(i); i++) {
          if(node == child) {
            this.formComponents[i].splice(i, 1);
            break;
          }
        }
        this.element.removeChild(child.getElement());
      },
      getChild: function(i) {
        return this.children[i];
      },
    
      // Implement the GalleryItem interface.
      
      hide: function() {
        for(var node, i = 0; node = this.getChild(i); i++) {
          node.hide();
        }
        this.element.style.display = 'none';
      },
      show: function() {
        this.element.style.display = 'block';
        for(var node, i = 0; node = this.getChild(i); i++) {
          node.show();
        }    
      },
      
      // Helper methods.
      
      getElement: function() {
        return this.element;
      }
    };
    
    第三步:创建GalleryImage类
    var GalleryImage = function(src) { // implements Composite, GalleryItem
      this.element = document.createElement('img');
      this.element.className = 'gallery-image';
      this.element.src = src;
    }
    
    GalleryImage.prototype = {
    
      // Implement the Composite interface.
    
      add: function() {},       // This is a leaf node, so we don't
      remove: function() {},    // implement these methods, we just
      getChild: function() {},  // define them.
    
      // Implement the GalleryItem interface.
      
      hide: function() {
        this.element.style.display = 'none';
      },
      show: function() {
        this.element.style.display = ''; // Restore the display attribute to its 
                                         // previous setting.
      },
      
      // Helper methods.
      
      getElement: function() {
        return this.element;
      }
    };
    
    第四步:使用
    var topGallery = new DynamicGallery('top-gallery');
    
    topGallery.add(new GalleryImage('/img/image-1.jpg'));
    topGallery.add(new GalleryImage('/img/image-2.jpg'));
    topGallery.add(new GalleryImage('/img/image-3.jpg'));
    
    var vacationPhotos = new DynamicGallery('vacation-photos');
    
    for(var i = 0; i < 30; i++) {
      vacationPhotos.add(new GalleryImage('/img/vac/image-' + i + '.jpg'));
    }
    
    topGallery.add(vacationPhotos);
    topGallery.show();      // Show the main gallery,
    vacationPhotos.hide();  // but hide the vacation gallery.
    总结:在这个例子中,我们就可以把DynamicGallery当作是上面那个结构图的Composite,GalleryImage当作是Leaf来理解了。DynamicGallery的add方法可以添加DynamicGallery,这样我们就可以创造出不同的结构了
     
  • 相关阅读:
    es6之更优雅的条件语句
    html 提取 公用部分
    jQuery 新添加元素事件绑定无效
    关于ie6块元素行内元素转换
    git 入门级使用
    vim入门级使用
    git安装配置
    学习使用mac
    Angular常用标记
    npm/bower/brew
  • 原文地址:https://www.cnblogs.com/oadaM92/p/4374743.html
Copyright © 2011-2022 走看看