zoukankan      html  css  js  c++  java
  • JS设计模式——9.组合模式

    组合模式概述

    组合模式是一种专为创建Web上的动态用户界面量身定制的模式.使用这种模式可以用一条命令在多个对象上激发复杂的递归的行为.

    它可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历.所有组合对象都实现了一个用来获得子对象的方法.

    组合对象有两种结构组合对象和叶对象,如下:

                   组合对象

                 /       |      

        组合对象 叶对象 组合对象

         /                         /         

    叶对象 叶对象     叶对象 叶对象  

    组合模式的适用条件

    1. 存在一批组织成某种层次体系的对象(具体的结构在开发期间可能无法得知)
    2. 希望这批对象或其中的一部分对象实施一个操作.

    组合模式擅长于对大批对象进行操作.它专为组织这类对象并把操作从一个层次向下一个层次传递而设计.

    示例:表单验证

    现在我们来设计一个表单,可以保存,恢复和验证其中的数据,但是这个表单中元素的内容和数目都是完全未知的,而且会因用户而异.

    我们将input,select,textarea作为叶对象,所有的input组成组合对象,所有的select和textarea组成组合对象,所有的组合对象再组成最终的组合对象.

    Task1:创建一个动态表单并实现save和validate操作

    我们不想为表彰元素的每一种可能组合编写一种方法,而是决定让这两个方法与表单域自身关联起来.也就是说让每个域都知道如何保存和验证自己.

    这里的难点在于如何同时在所有域上执行这些操作.我们不想用迭代的方法,而是用组合模式.

    最顶层的组合对象将在其所有子对象上递归调用save方法.

    //
    var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
    var FormItem = new Interface('FormItem', ['save']);
    var CompositeForm = function(id, method, action){
        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.getElment());
    };
    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);
                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;
    };
    //
    var Field = function(id){
        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){
        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);
    InputField.prototype.getValue = function(){
        return this.input.value;
    };
    var TextareaField = function(id, label){
        Field.call(this, id);
    
        this.textarea = document.createElement('textarea');
        this.textarea.id = id;
    
        this.label = document.createElement('label');
        var labelTexrarea = document.createTextNode(label);
        this.label.appendChild(labelTexrarea);
    
        this.element = document.createElement('div');
        this.element.className = 'input-field';
        this.element.appendChild(this.label);
        this.element.appendChild(this.textarea);
    }
    extend(TextareaField, Field);
    TextareaField.prototype.getValue = function(){
        return this.textarea.value;
    };
    var SelectField = function(id, label){
        Field.call(this, id);
    
        this.select = document.createElement('select');
        this.select.id = id;
    
        this.label = document.createElement('label');
        var labelTexrarea = document.createTextNode(label);
        this.label.appendChild(labelTexrarea);
    
        this.element = document.createElement('div');
        this.element.className = 'input-field';
        this.element.appendChild(this.label);
        this.element.appendChild(this.select);
    }
    extend(SelectField, Field);
    SelectField.prototype.getValue = function(){
        return this.select.options[this.select.selectedIndex].value;
    };

    Task2:汇合起来

    这是组合模式大放光彩的地方.无论有多少个表单域,对整个组合对象执行操作只需一个函数调用即可.

    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));
    contactForm.add(new InputField('zip', 'Zip'));
    contactForm.add(new TextareaField('comments', 'Comments'));
    var stateArray = [{'al': 'Alabama'},...];
    addEvent(window, 'unload', contactForm.save);

    Task3:向FormItem添加操作

    1.修改接口

    var FormItem = new Interface('FormItem', ['save', 'restore']);

    2.在叶对象类上实现这些操作

    Field.prototype.restore = function(){
        this.element.value = getCookie(this.id);
    };

    3.为组合对象添加同样的操作

    CompositeForm.prototype.restore = function(){
        for(var i= 0, len=this.formComponents.length; i<len; i++){
            this.formComponents[i].restore();
        }
    };

    4.注册事件

    addEvent(window, 'load', contactForm.restore);

    Task4:向层次体系中添加类

    到目前为止只有一个组合对象类.如果设计目标要求对操作的调用有更多粒度上的控制,那么,可以添加更多层次的组合对象类,而不必改变其他类.

    在层次体系中创建新的层次是一个更好的选择.我们可以把域组织在域集中,每一个域集都是一个实现FormItem接口的组合对象.

    var CompositeFieldset = function(id, legendText){
        this.components = {};
        this.element = document.createElement('fieldset');
        this.element.id = id;
        if(legendText){
            this.legend = document.createElement('legend');
            this.legend.appendChild(document.createTextNode(legendText));
            this.element.appendChild(this.legend);
        }
    };
    CompositeFieldset.prototype.save = function(){
        for(var id in this.components){ //在中间新建了一层,那么就要相应的增加递归的操作,只要不是叶对象,都应该有类似的操作
            if(!this.components.hasOwnProperty(id)) continue;
            this.components[id].save();
        }
    };
    CompositeFieldset.prototype.restore = function(){
        for(var id in this.components){
            if(!this.components.hasOwnProperty(id)) continue;
            this.components[id].restore();
        }
    };
    CompositeFieldset.prototype.getElement = function(){
        return this.element;
    };
    var nameFieldset = new CompositeFieldset('name-fieldset');
    nameFieldset.add(new InputField('first-name', 'First Name'));
    nameFieldset.add(new InputField('last-name', 'Last Name'));
    contactForm.add(nameFieldset);

    现在我们用域集对一部分域进行了组织.也可以直接把域加入表单之中,这是因为表单不在乎子对象究竟是组合对象还是叶对象.

    组合模式之利

    1. 不必编写大量手工遍历数组或其他数据结构的粘合代码.
    2. 各个对象之间非常松散,促进了代码的重用.
    3. 形成了一个出色的层次体系,每当顶层组合对象执行一个操作时,实际上是在对整个结构进行深度优先的搜索以查找节点.这样查找,添加和删除都是很容易的.

    组合模式之弊

    由于组合对象调用的任何操作都会被传递到它的所有子对象,如果这个层次体系很大的话,系统的性能将会受到影响.

  • 相关阅读:
    地铁开发进度02
    地铁开发进度02
    CSS 笔记——列表表格
    CSS 笔记——列表表格
    前端核心代码保护技术面面观
    激活函数总结
    data argumentation 数据增强汇总
    YOLOV4
    import android.support.annotation.NonNull;报错
    关于import android.support.v4.app.ContextCompat;找不到contextcompat的解决方法
  • 原文地址:https://www.cnblogs.com/JChen666/p/3645038.html
Copyright © 2011-2022 走看看