组合模式

    组合模式大概是设计模式里面使用最为广泛的模式之一了,模式本身理解起来也比较简单,以至于可以毫不费力的写出一个能用的组合模式伪代码。

该模式解决了什么问题?

    组合模式统一对待复合对象与单个对象。抽象出统一的命令执行接口,这种多态性使得客户端在编写代码的时候可以不加区分当前要操作的是复合对象还是单个对象。

组合模式基本实现

function inherits(child, sup) {
    child.prototype = Object.create(sup.prototype, {
        constructor: {
            value: child,
            enumerable: false,
            configurable: true,
            writable: true
        }
    });
}
//组合对象与叶子对象都集成自component对象
function Component() {

}

Component.prototype.execute = function(context) {
    throw new Error('Component function must be override');
};

Component.prototype.add = function(command) {
    throw new Error('Component function must be override');
};

Component.prototype.remove = function(command) {
    throw new Error('Component function must be override');
};

function Composite() {
    this.childs = [];
    Component.call(this);
}

inherits(Composite, Component);

Composite.prototype.execute = function(context) {
    for(var i = 0; i < this.childs.length; i++) {
        this.childs[i].execute(context);            
    }
};

Composite.prototype.add = function(child) {
    this.childs.push(child);
};

Composite.prototype.remove = function(child) {
    for(var i = 0; i < this.childs.length; i++) {
        if(child == this.childs[i]) {
            this.childs.splice(i, 1);           
        }
    }
};
//叶子节点不能添加子对象
function Leaf(command) {
    Component.call(this);
    this.command = command;
}

inherits(Leaf, Component);

Leaf.prototype.execute = function(context) {
    if(!this.command) return;   
    this.command.execute(context);
};

以上是传统面向对象语言对于组合模式的一个规范实现,由于Javascript这种动态语言本身的一些限制,我们通过在基类抛出异常的方式来阻止客户端往Leaf对象中添加子对象,代码整体来看会有一些冗余,下面来写一个简单的实现。

Javascript下的实现

    组合模式无非就是需要一个列表来存放子对象,这些对象往往是一些需要被执行的命令(常和命令模式组合运用)。所以我们可以写成如下形式:

var CompositeCommand = function() {
    return {
        commandList: [],
        add: function(command) {
            this.commandList.push(command);
        },
        execute: function(context) {
            for(var i = 0, length = this.commandList.length; i < length; i++) {
                var command = this.commandList[i];
                command.execute(context);
            }
        }
    };
}

var openWindowCommand = {
    execute: function(ctx) {
        console.log('openWindowCommand');
    }
};

var playMusicCommand = {
    execute: function(ctx) {
        console.log('playMusicCommand');
    }
};

var closeDoorCommand = {
    execute: function(ctx) {
        console.log('closeDoorCommand');
    }
};

var composite1 = CompositeCommand();
    composite1.add(playMusicCommand);
    composite1.add(openWindowCommand);
var composite = CompositeCommand();
    composite.add(composite1);
    composite.add(closeDoorCommand);
    composite.execute();    

输出:

playMusicCommand
openWindowCommand
closeDoorCommand

组合模式常用的一般情况

    组合模式适用于对一组对象执行同一类操作,而这组对象有可能是包含关系,也可以是单个个体。常用的情形如:文件扫描,我们扫描文件的时候并不关心当前是文件夹或者是单个文件,都可以统一执行scan接口,当然前提是我们已经得到了一组文件对象的依赖关系。再如,GUI绘制界面的时候,通常会先绘制主窗口渲染背景,然后再绘制叠加在窗口上的各个子控件,而这些子控件有可能又包含一些子控件。基于GUI控件的依赖层次关系。也非常适合使用组合模式:

var createDialog = function () {
    return {
        bgimg: 'blues.png',
        children: [],
        addChild: function(child) {
            this.children.push(child);
        },
        paintSelf: function(context) {
            console.log('paint dialog');
        },
        paint: function(context) {
            this.paintSelf(context);
            for(var i = 0, length = this.children.length; i< length; i++) {
                var child = this.children[i];
                    child.paint(context);
            }
        }   
    };
}

var createButton = function() {
    return {
        paint: function(ctx) {
            console.log('paint button');
        }
    };
};

var createListView = function() {
    return {
        children: [],
        addChild: function(item) {
            this.children.push(item);
        },
        paint: function(ctx) {
            console.log('paint listview');
                for(var i = 0; i < this.children.length; i++) {
                this.children[i].paint(ctx);
        }
        }
    };
};

var createListItem = function() {
    return {
        paint: function(ctx) {
            console.log('paint list item');
        }
    };
};

var window = createDialog();
var dialog1 = createDialog();
    dialog1.addChild(createButton());
    window.addChild(dialog1);
var dialog2 = createDialog();
var listview = createListView();
    listview.addChild(createListItem());
    dialog2.addChild(listview);
    window.addChild(dialog2);

    window.paint({});

输出

paint dialog
paint dialog
paint button
paint dialog
paint listview
paint list item

后记

  • 1.复合对象与单个对象不是父子关系,二是聚合(has-A)关系。
  • 2.节点必须有统一的操作接口
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值