组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

  • 透明方式,也就是说在Commponent中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Commponent接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的。
  • 安全方式,也就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。

透明式的组合模式类图:

安全式组合模式的类图:

组合模式中涉及到三个角色:

  • 抽象构件(Component)角色:这是一个抽象角色,上面实现中Graphics充当这个角色,它给参加组合的对象定义出了公共的接口及默认行为,可以用来管理所有的子对象(在透明式的组合模式是这样的)。在安全式的组合模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出。
  • 树叶构件(Leaf)角色:树叶对象时没有下级子对象的对象,上面实现中Line和Circle充当这个角色,定义出参加组合的原始对象的行为
  • 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象,上面实现中ComplexGraphics充当这个角色,树枝对象给出所有管理子对象的方法实现,如Add、Remove等。

组合模式的使用场景

在以下情况下应该考虑使用组合模式:

  1. 需要表示一个对象整体或部分的层次结构。
  2. 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

C#组合模式:

namespace 组合模式
{
class Program
{
static void Main(string[] args)
{
ConcreteCompany root = new ConcreteCompany("北京总公司");
root.Add(new HRDepartment("总公司人力资源部"));
root.Add(new FinanceDepartment("总公司财务部")); ConcreteCompany comp = new ConcreteCompany("上海华东分公司");
comp.Add(new HRDepartment("华东分公司人力资源部"));
comp.Add(new FinanceDepartment("华东分公司财务部"));
root.Add(comp); ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
comp1.Add(new HRDepartment("南京办事处人力资源部"));
comp1.Add(new FinanceDepartment("南京办事处财务部"));
comp.Add(comp1); ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
comp2.Add(new HRDepartment("杭州办事处人力资源部"));
comp2.Add(new FinanceDepartment("杭州办事处财务部"));
comp.Add(comp2); Console.WriteLine("\n结构图:"); root.Display(1); Console.WriteLine("\n职责:"); root.LineOfDuty(); Console.Read();
}
} abstract class Company
{
protected string name; public Company(string name)
{
this.name = name;
} public abstract void Add(Company c);//增加
public abstract void Remove(Company c);//移除
public abstract void Display(int depth);//显示
public abstract void LineOfDuty();//履行职责 } class ConcreteCompany : Company
{
private List<Company> children = new List<Company>(); public ConcreteCompany(string name)
: base(name)
{ } public override void Add(Company c)
{
children.Add(c);
} public override void Remove(Company c)
{
children.Remove(c);
} public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name); foreach (Company component in children)
{
component.Display(depth + 2);
}
} //履行职责
public override void LineOfDuty()
{
foreach (Company component in children)
{
component.LineOfDuty();
}
} } //人力资源部
class HRDepartment : Company
{
public HRDepartment(string name)
: base(name)
{ } public override void Add(Company c)
{
} public override void Remove(Company c)
{
} public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
} public override void LineOfDuty()
{
Console.WriteLine("{0} 员工招聘培训管理", name);
}
} //财务部
class FinanceDepartment : Company
{
public FinanceDepartment(string name)
: base(name)
{ } public override void Add(Company c)
{
} public override void Remove(Company c)
{
} public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
} public override void LineOfDuty()
{
Console.WriteLine("{0} 公司财务收支管理", name);
} }
}

js组合模式:

<html>
<body>
<button id="button">按我</button>
</body>
<script>
var MacroCommand = function(){
return{
commandsList:[],
add:function(command){
this.commandsList.push(command);
},
execute:function(){
for(var i=0,command;command=this.commandsList[i++];){
command.execute();
}
}
}
}; var openAcCommand = {
execute:function(){
console.log('打开空调');
}
}; /*****************家里的电视和音响是连接在一起的,所以可以用一个宏命令来组合打开电视和打开音响的命令********************/ var openTvCommand = {
execute:function(){
console.log('打开电视');
}
}; var openSoundCommand = {
execute:function(){
console.log('打开音响');
}
}; var macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand); /***********关门、打开电脑和登录QQ的命令**************/ var closeDoorCommand = {
execute:function(){
console.log('关门');
}
}; var openPcCommand = {
execute:function(){
console.log('开电脑');
}
}; var openQQCommand = {
execute:function(){
console.log('登录QQ');
}
}; var macroCommand2 = MacroCommand();
macroCommand2.add(closeDoorCommand);
macroCommand2.add(openPcCommand);
macroCommand2.add(openQQCommand); /******************现在把所有的命令组合成一个“超级命令”*********************/ var macroCommand = MacroCommand();
macroCommand.add(openAcCommand);
macroCommand.add(macroCommand1);
macroCommand.add(macroCommand2); /**************最后给遥控器绑定“超级命令”**********************/ var setCommand = (function(command){
document.getElementById('button').onclick = function(){
command.execute();
}
})(macroCommand); </script>
</html>

javascript中实现组合模式的难点在于要保证组合对象和叶对象对象拥有同样的方法,这通常需要用鸭子类型的思想对它们进行接口检查。

组合对象可以拥有子节点,叶对象下面就没有子节点,所以我们也许会发生一些误操作,比如试图往叶对象中添加子节点,解决方案通常是给叶对象叶增加add方法,并且在调用这个方法时,抛出一个异常来及时提醒客户,代码如下:

var MacroCommand = function(){
return{
commandsList:[],
add:function(command){
this.commandsList.push(command);
},
execute:function(){
for(var i=0,command;command=this.commandsList[i++];){
command.execute();
}
}
}
}; var openTvCommand = {
execute:function(){
console.log('打开电视');
},
add:function(){
throw new Error('叶对象不能添加子节点');
}
}; var macroCommand = MacroCommand(); macroCommand.add(openTvCommand);
openTvCommand.add(macroCommand); //Uncaugth Error:叶对象不能添加子节点

js组合模式的更多相关文章

  1. javascript设计模式(张容铭) 第14章 超值午餐-组合模式 学习笔记

    JS 组合模式更常用于创建表单上,比如注册页面可能有不同的表单提交模块.对于这些需求我们只需要有基本的个体,然后通过一定的组合即可实现,比如下面这个页面样式(如图14-2所示),我们来用组合模式实现. ...

  2. js架构设计模式——你对MVC、MVP、MVVM 三种组合模式分别有什么样的理解?

    你对MVC.MVP.MVVM 三种组合模式分别有什么样的理解? MVC(Model-View-Controller)MVP(Model-View-Presenter)MVVM(Model-View-V ...

  3. [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表

    所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理, ...

  4. js设计模式(六)---组合模式

    组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构.除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性.基本图例 1.组合模式 ...

  5. JS设计模式(7)组合模式

    什么是组合模式? 定义:1.将对象组合成树形结构以表示"部分-整体"的层次结构.2.组合模式使得用户对单个对象和组合对象的使用具有一致性.3.无须关心对象有多少层,调用时只需在根部 ...

  6. js设计模式-组合模式

    组合模式是一种专为创建web上的动态用户界面而量身定制的模式.使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更容易维护,而那些复杂行为则被委托给各个对象. ...

  7. JS常用的设计模式(13)——组合模式

    组合模式又叫部分-整体模式,它将所有对象组合成树形结构.使得用户只需要操作最上层的接口,就可以对所有成员做相同的操作. 一个再好不过的例子就是jquery对象,大家都知道1个jquery对象其实是一组 ...

  8. js设计模式(4)---组合模式

    0.前言 今天是建党节,新疆那边又开始了闹腾.作为立志成为码农的我,现在已经从一个大愤青淡化为一个小愤青,对这些国家民生大事不在血气喷发,转而把经历发泄在技术问题上面,因而在扯一篇随笔吧,把无处发泄的 ...

  9. JS设计模式——9.组合模式

    组合模式概述 组合模式是一种专为创建Web上的动态用户界面量身定制的模式.使用这种模式可以用一条命令在多个对象上激发复杂的递归的行为. 它可以用来把一批子对象组织成树形结构,并且使整棵树都可被遍历.所 ...

随机推荐

  1. Git使fork项目与源项目保持一致方法

    Github上经常干的一件事情是看到好的项目,总会fork到自己的项目列表里,但是源项目如果更新了,怎么同步到我们自己的fork项目呢? 操作如下: 先clone自己的fork项目到本地工程目录, g ...

  2. JavaScript方法splice()和slice()

    1 splice() 1.1 说明 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目.该方法会改变原始数组.Link 1.2 语法 arrayObject.splice(inde ...

  3. js实现还可输入多少个字

    // 还可输入的字数个数 function checkLength(obj, len) { var maxChars = len; if (obj.value.length <= maxChar ...

  4. Python报错——module 'scipy.misc' has no attribute 'imresize'

    报错是因为要安装PIL库,库名现在为Pillow,在命令行上安装即可: pip3 install Pillow

  5. JAVA中遍历Map和Set方法,取出map中所有的key

    Java遍历Set集合 1.迭代器遍历: Set<String> set = new HashSet<String>(); Iterator<String> it ...

  6. Python基础(9)_生成器(yield表达式形式)、面向过程编程

    一.yield表达式形式 1 #装饰器,初始化含yield表达式的生成器 def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwa ...

  7. 跟踪 twisted 里deferred 的Callback

    twisted 提供了 deferred 机制,而关键点就是回调.通过查看deferred 源码 (version 8.2.0)我们可以 看到 deferred的addCallback是怎么工作的,以 ...

  8. SQL Server 存储过程 分页查询

    Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø ...

  9. $Android自定义控件在不同状态下的属性

    在写代码的时候,有时候需要控件在不同状态下显示不同的外观,比如在按钮按下的时候要变颜色,EditText获取焦点时候边框要变颜色等.那么下面就来梳理一下这些是怎么实现的. (一)按钮按下时候变颜色 1 ...

  10. JSP笔记01——尝试

    JSP ————> servlet 我的第1个Java Web应用程序——index.jsp 我的第2个Java Web应用程序——welcome-file 我的第3个Java Web应用程序— ...