zoukankan      html  css  js  c++  java
  • JavaScript设计模式(5)-组合模式

    组合模式

    1. 适合使用组合模式的条件:

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

    2. 注意点:

    • 组合对象与其所有子对象具有相同的接口,但是叶对象并没有继承其上一级组合对象,不是超类与子类的关系

    3. 例子:表单信息保存及恢复

    可以指定整个表单保存输入数据,及恢复保存的数据,也可以只保存指定的一部分数据

    <style>
        form {
            border: 6px double #9c9c9c;
            padding: 10px;
            margin: 10px;
        }
    </style>
    
    <body>
        <div class="box">
            <div class="child"></div>
        </div>
        <div id="feed-readers"></div>
    </body>
    
    <script>
    
    // 用于实现类似继承的方法,这个是简化版,可以去前面的文章:JavaScript设计模式(1) 查看
    function extend(subClass, superClass) {
        var F = function() {}
        F.prototype = superClass.prototype;
        subClass.prototype = new F();
        subClass.prototype.constructor = subClass;
    }
    
    // var Composite = new Interface('Composite', ['add', 'remove', 'getChild'])
    // var FormItem = new Interface('FormItem', ['save', 'restore'])
    
    // 存储要保存的数据
    var DATA = {}
    
    /**
    * [CompositeForm 表单类]
    */
    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 || '#';
    
        var bt1 = document.createElement('button')
        var bt2 = document.createElement('button')
        bt1.innerHTML = 'save'
        bt2.innerHTML = 'restore'
        bt1.addEventListener('click', this.save.bind(this))
        bt2.addEventListener('click', this.restore.bind(this))
        this.element.appendChild(bt1)
        this.element.appendChild(bt2)
    };
    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);
                child.getElement().remove()
                break;
            }
        }
    };
    CompositeForm.prototype.getChild = function(i) {
        return this.formComponents[i];
    };
    CompositeForm.prototype.save = function(e) {
        e.preventDefault()
        DATA = {}
        for(var i=0, len=this.formComponents.length; i<len; i++) {
            this.formComponents[i].save()  // 组合模式遍历各个子对象并对他们调用同样的方法
        }
        console.log(DATA)
    };
    CompositeForm.prototype.restore = function(e) {
        e.preventDefault()
        for(var i=0, len=this.formComponents.length; i<len; i++) {
            this.formComponents[i].restore()
        }
    }
    CompositeForm.prototype.getElement = function() {
        return this.element;
    };
    
    /**
    * [CompositeFieldset 表单域类]
    */
    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);
        }
    
        var bt1 = document.createElement('button')
        var bt2 = document.createElement('button')
        bt1.innerHTML = 'save'
        bt2.innerHTML = 'restore'
        bt1.addEventListener('click', this.save.bind(this))
        bt2.addEventListener('click', this.restore.bind(this))
        this.element.appendChild(bt1)
        this.element.appendChild(bt2)
    }
    CompositeFieldset.prototype.add = function(child) {
        // Interface.ensureImplements(child, Composite, FormItem)
        this.components[child.id] = child;
        this.element.appendChild(child.getElement())
    }
    CompositeFieldset.prototype.remove = function(child) {
        delete this.components[child.getElement().id];
        child.getElement().remove()
    }
    CompositeFieldset.prototype.getChild = function(id) {
        if(this.components[id] !== undefined) {
            return this.components[id]
        }else {
            return null
        }
    }
    CompositeFieldset.prototype.getElement = function() {
        return this.element;
    }
    CompositeFieldset.prototype.save = function(e) {
        if(e) e.preventDefault()
        for(var id in this.components) {
            if(!this.components.hasOwnProperty(id)) continue;
            this.components[id].save()
        }
        console.log(DATA)
    }
    CompositeFieldset.prototype.restore = function(e) {
        if(e) e.preventDefault()
        for(var id in this.components) {
            if(!this.components.hasOwnProperty(id)) continue;
            this.components[id].restore()
        }
    }
    
    /**
    * [Field 叶子类,实现空方法]
    */
    var Field = function(id) {
        this.id = id;
        this.element = null;
    };
    Field.prototype.add = function(){};
    Field.prototype.remove = function(){};
    Field.prototype.getChild = function(){};
    Field.prototype.save = function() {
        DATA[this.id] = this.getValue()
    }
    Field.prototype.restore = function() {
        this.input.value = DATA[this.id]
    }
    Field.prototype.getElement = function() {
        return this.element;
    }
    Field.prototype.getVulue = function() {
        throw new Error('Unsupported operation on the class Field')
    }
    
    /**
    * [InputField 输入类,继承至 Field]
    */
    var InputField = function(id, label) {
        Field.call(this, id);
    
        this.input = document.createElement('input');
        this.input.id = id;
        this.label = document.createElement('label');
        this.label.innerHTML = label;
        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
    }
    
    // usage
    var contactForm = new CompositeForm('contact-form', 'POST', 'www.baidu.com');
    
    var input1 = new InputField('input1', 'input1')
    var input2 = new InputField('input2', 'input2')
    var input3 = new InputField('input3', 'input3')
    var input4 = new InputField('input4', 'input4')
    
    var firstFieldset = new CompositeFieldset('first-fieldset', 'first-fieldset')
    firstFieldset.add(input1)
    firstFieldset.add(input2)
    
    var secondFieldset = new CompositeFieldset('second-fieldset', 'second-fieldset')
    secondFieldset.add(input3)
    secondFieldset.add(input4)
    
    contactForm.add(firstFieldset)
    contactForm.add(secondFieldset)
    
    document.getElementById('feed-readers').appendChild(contactForm.getElement())
    
    </script>
    

    4. 例子:图片库

    对照片进行隐藏和显示

    <style>
        .dynamic-gallery {
            display: inline-block;
            margin: 30px;
            padding: 5px;
            border: 1px solid;
        }
        .gallery-image {
             100px;
            border: 1px solid;
            padding: 5px;
            margin: 5px;
        }
        button {
            float: left;
        }
    </style>
    
    <body>
        <div id="feed-readers"></div>
    </body>
    <script>
    
    // var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
    // var GalleryItem = new Interface('GalleryItem', ['hide', 'show']);
    
    var DynamicGallery = function(id) {
        this.children = [];
        this.element = document.createElement('div')
        this.element.id = id
        this.element.className = 'dynamic-gallery';
        this.element.appendChild(document.createTextNode(id))
    }
    DynamicGallery.prototype = {
        add: function(child) {
            // Interface.ensureImplement(chlid, Composite, GalleryItem);
            this.children.push(child);
            this.element.appendChild(child.getElement())
        },
        remove: function(child) {
            for(var i=0; i<this.children.length ;i++) {
                if(this.getChild(i) == child) {
                    this.children.splice(i, 1);
                    child.remove()
                    break;
                }
            }
        },
        getChild: function(i) {
            if(this.children[i] !== undefined) {
                return this.children[i]
            }else {
                return null
            }
        },
        hide: function() {
            for(var i=0; i<this.children.length; i++) {
                this.getChild(i).hide()
            }
            // this.element.style.display = 'none'
        },
        show: function() {
            // this.element.style.display = 'inline-block'
            for(var i=0; i<this.children.length; i++) {
                // if(this.getChild(i) instanceof GalleryImage)
                    this.getChild(i).show()
    
            }
        },
        getElement: function() {
            return this.element
        }
    }
    
    var GalleryImage = function(src) {
        this.element = document.createElement('img');
        this.element.className = 'gallery-image';
        this.element.src = src;
    }
    GalleryImage.prototype = {
        add: function(){},
        remove: function(){
            this.element.remove()
        },
        getChild: function(){},
    
        hide: function() {
            this.element.style.display = 'none'
        },
        show: function() {
            this.element.style.display = ''
        },
        getElement: function() {
            return this.element;
        }
    }
    
    var img1 = new GalleryImage('./images/bg4.jpg')
    var img2 = new GalleryImage('./images/bg4.jpg')
    var img3 = new GalleryImage('./images/bg4.jpg')
    var img4 = new GalleryImage('./images/bg4.jpg')
    var img5 = new GalleryImage('./images/bg4.jpg')
    var img6 = new GalleryImage('./images/bg4.jpg')
    var img7 = new GalleryImage('./images/bg4.jpg')
    var img8 = new GalleryImage('./images/bg4.jpg')
    
    var firstGallery = new DynamicGallery('first-gallery');
    firstGallery.add(img1)
    firstGallery.add(img2)
    
    var secondGallery = new DynamicGallery('second-gallery');
    secondGallery.add(img3)
    secondGallery.add(img4)
    
    var fourGallery = new DynamicGallery('fourGallery-gallery');
    fourGallery.add(img5)
    fourGallery.add(img6)
    
    firstGallery.add(fourGallery)
    
    var wrap = new DynamicGallery('wrap')
    wrap.add(img7)
    wrap.add(img8)
    wrap.add(firstGallery)
    wrap.add(secondGallery)
    
    document.getElementById('feed-readers').appendChild(wrap.getElement())
    wrap.hide()
    
    </script>
    

    5. 组合模式的利与弊

    • 利:
      • 不必编写大量手工遍历数组或其他数据结构的粘合代码,只需对最顶层对象执行操作就能把操作传递下去
      • 各个对象之间的耦合非常松散,这促进了代码的重用,也有利于代码重构
      • 用组合模式组织起来的对象形成了一个出色的层次体系,在这个层次体系中添加,删除和查找结点都非常容易
    • 弊:
      • 如果层次体系结构很大的话,系统的性能将会受到影响。
      • 如果组合对象类和节点类都被用作 HTML 元素的包装工具,则需要准守 HTML 的使用规则来进行组合对象的嵌套
      • 需要进行接口检查,确保添加的组合对象实现了对应的方法

    注意

    转载、引用,但请标明作者和原文地址

  • 相关阅读:
    codevs1231 最优布线问题
    P1352 没有上司的舞会——树形DP入门
    codevs1961 躲避大龙
    codevs2833 奇怪的梦境
    linux 内存管理——内核的shmall 和shmmax 参数
    删除共享内存后key为0x00000000的问题
    redis命令之lrange
    vim配置vimrc详解
    chmod chgrp chown
    localtime和localtime_r
  • 原文地址:https://www.cnblogs.com/CccZss/p/8492531.html
Copyright © 2011-2022 走看看