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 {
        width: 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 的使用规则来进行组合对象的嵌套
    • 需要进行接口检查,确保添加的组合对象实现了对应的方法

注意

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

转载于:https://www.cnblogs.com/CccZss/p/8492531.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值