什么是组合模式呢?简单来说组合模式就是将对象合成树形结构以表示“部分整体”的层次结构,组合模式使用户对单个对象和组合对象使用具有一致性。

组合模式(Composite Pattern)有时候又叫部分-整体模式,有的地方也翻译成"组成模式"、"合成模式",它使我们在树型结构的问题中,模糊了单个对象和组合对象的概念,客户程序可以像处理单个对象一样处理组合对象,从而使得单个对象和组合对象的内部结构解耦。

组合模式让你可以优化处理递归或分级数据结构。关于分级数据结构的一个经典例子就是电脑中的文件系统。文件系统由目录和文件组成,所有目录都可以有子目录和文件。实际上文件系统就是按照递归来组织的,那么就可以组合模式来描述这种结构。

适用性

以下情况下适用Composite模式:

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

组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。

组合模式的实现根据实现接口的区别分为两种形式,分别称为安全模式和透明模式。组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(如:add,remove,getchild等)。

透明方式:

作为第一种选择,在Component里面声明所有的用来管理子对象的方法,包括Add(),Remove(),以及GetChild()方法。这样做的好处是所有的构件类都有相同的接口,在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以等同的对待所有对象,这就是透明形式的合成模式。

这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此Add(),Remove(),GetChild()方法没有意义,是在编译时期不会出错,而只会在运行时期出错。

安全方式

安全方式是在Composite类里面声明所有的用来管理子对象的方法,这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子对象的方法,因此,如果客户端对树叶类对象使用这些方法,程序会在编译时期出错。 这个方式的缺点是不够透明,因为树叶类和合成类将具有不同的接口。

安全方式对应的结构

                    

这种形式涉及到三个角色:

  1. 抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象定义出公共接口及默认行为,可以用来管理所有的子对象。在安全形式的合成模式里,抽象构件角色并不定义出管理子对象的方法,这一定义由树枝构件定义。
  2. 树叶构件(Leaf)角色: 树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。
  3. 树枝构件(Composite)角色: 代表参加组合的有下级子对象的对象。树枝给出所有的管理子对象的方法,如Add(),Remove(),GetChild()等。

实现代码:

  public interface Component
{
public void printStruct(String preStr);
} public class Composite implements Component
{
private List<Component> childComponents = new ArrayList<Component>();
private String name;
public Composite(String name)
{
this.name = name;
} public void addChild(Component child)
{
childComponents.add(child);
} public void removeChild(int index)
{
childComponents.remove(index);
} public List<Component> getChild()
{
return childComponents;
} @Override
public void printStruct(String preStr)
{
System.out.println(preStr + "+" + this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null)
{
preStr += " ";
for(Component c : childComponents)
{
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
} public class Leaf implements Component
{
private String name;
public Leaf(String name)
{
this.name = name;
} @Override
public void printStruct(String preStr)
{
System.out.println(preStr + "-" + name);
}
} public class Client
{
public static void main(String[]args)
{
Composite root = new Composite("服装");
Composite c1 = new Composite("男装");
Composite c2 = new Composite("女装"); Leaf leaf1 = new Leaf("衬衫");
Leaf leaf2 = new Leaf("夹克");
Leaf leaf3 = new Leaf("裙子");
Leaf leaf4 = new Leaf("套装"); root.addChild(c1);
root.addChild(c2);
c1.addChild(leaf1);
c1.addChild(leaf2);
c2.addChild(leaf3);
c2.addChild(leaf4); root.Display("");
}
}

可以看出,树枝构件类(Composite)给出了addChild()、removeChild()以及getChild()等方法的声明和实现,而树叶构件类则没有给出这些方法的声明或实现。这样的做法是安全的做法,由于这个特点,客户端应用程序不可能错误地调用树叶构件的聚集方法,因为树叶构件没有这些方法,调用会导致编译错误。

  安全式合成模式的缺点是不够透明,因为树叶类和树枝类将具有不同的接口。

透明方式对应的结构

 与安全式的合成模式不同的是,透明式的合成模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定接口。

这种形式涉及到三个角色:

  1. 抽象构件(Component)角色:这是一个抽象角色,它给所有参加组合的对象规定一个固定的接口,规范共有的接口及默认行为.
  2. 树叶构件(Leaf)角色:代表参加组合的树叶对象,定义出参加组合的原始对象的行为,树叶类会给出Add(),Remove(),以及GetChild()之类的用来管理子类对象的方法的实现。
  3. 树枝构件角色(Composite)角色: 代表参加组合的有子对象的对象。定义出这样的对象的行为。

源代码

下面是抽象构件角色类

  public abstract class Component
{
public abstract void printStruct(String preStr);
public void addChild(Component child)
{
// 缺省实现
throw new UnsupportedOperationException("对象不支持此功能");
} public void removeChild(int index)
{
// 缺省实现
throw new UnsupportedOperationException("对象不支持此功能");
} public List<Component> getChild()
{
// 缺省实现
throw new UnsupportedOperationException("对象不支持此功能");
}
}

下面是树枝构件角色类,此类将implements Conponent改为extends Conponent,其他地方无变化。

  public class Composite extends Component
{
private List<Component> childComponents = new ArrayList<Component>();
private String name;
public Composite(String name)
{
this.name = name;
} public void addChild(Component child)
{
childComponents.add(child);
} public void removeChild(int index)
{
childComponents.remove(index);
} public List<Component> getChild()
{
return childComponents;
} @Override
public void printStruct(String preStr)
{
// 先把自己输出
System.out.println(preStr + "+" + this.name);
//如果还包含有子组件,那么就输出这些子组件对象
if(this.childComponents != null)
{
preStr += " ";
for(Component c : childComponents)
{
//递归输出每个子对象
c.printStruct(preStr);
}
}
}
}

下面是树叶构件角色类,此类将implements Conponent改为extends Conponent,其他地方无变化。

  public class Leaf extends Component
{
private String name;
public Leaf(String name)
{
this.name = name;
} @Override
public void printStruct(String preStr)
{
System.out.println(preStr + "-" + name);
}
}

下面是客户端类的主要变化是不再区分Composite对象和Leaf对象。

  public class Client
{
public static void main(String[]args)
{
Component root = new Composite("服装");
Component c1 = new Composite("男装");
Component c2 = new Composite("女装"); Component leaf1 = new Leaf("衬衫");
Component leaf2 = new Leaf("夹克");
Component leaf3 = new Leaf("裙子");
Component leaf4 = new Leaf("套装"); root.addChild(c1);
root.addChild(c2);
c1.addChild(leaf1);
c1.addChild(leaf2);
c2.addChild(leaf3);
c2.addChild(leaf4); root.printStruct("");
}
}

可以看出,客户端无需再区分操作的是树枝对象(Composite)还是树叶对象(Leaf)了;对于客户端而言,操作的都是Component对象。

使用合成模式时考虑的几个问题:

  1. Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转换为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关系处理的是单个对象还是组合的对象容器。
  2. 将客户代码与复杂的对象容器解耦是合成模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口—----非对象容器的内部实现结构发生依赖关系。
  3. 有时候系统需要遍历一个树枝结构的子构件很多次,这时候可以考虑把遍历子构件的结构暂时存储在父构件里面作为缓存。
  4. Composite模式中,是将Add和Remove等和对象容器相关的方法定义在“表示抽象的Componont类”中,还是定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。

两种实现方法的选择

  这里所说的安全性合成模式是指:从客户端使用合成模式上看是否更安全,如果是安全的,那么就不会有发生误操作的可能,能访问的方法都是被支持的。

  这里所说的透明性合成模式是指:从客户端使用合成模式上,是否需要区分到底是“树枝对象”还是“树叶对象”。如果是透明的,那就不用区分,对于客户而言,都是Compoent对象,具体的类型对于客户端而言是透明的,是无须关心的。

  对于合成模式而言,在安全性和透明性上,会更看重透明性,毕竟合成模式的目的是:让客户端不再区分操作的是树枝对象还是树叶对象,而是以一个统一的方式来操作。

  而且对于安全性的实现,需要区分是树枝对象还是树叶对象。有时候,需要将对象进行类型转换,却发现类型信息丢失了,只好强行转换,这种类型转换必然是不够安全的。

  因此在使用合成模式的时候,建议多采用透明性的实现方式。 

原文参考 http://www.cnblogs.com/shaosks/archive/2012/03/26/2418065.html

http://www.cnblogs.com/java-my-life/archive/2012/04/17/2453861.html

组合模式Composite Pattern(转)的更多相关文章

  1. 浅谈设计模式--组合模式(Composite Pattern)

    组合模式(Composite Pattern) 组合模式,有时候又叫部分-整体结构(part-whole hierarchy),使得用户对单个对象和对一组对象的使用具有一致性.简单来说,就是可以像使用 ...

  2. 二十四种设计模式:组合模式(Composite Pattern)

    组合模式(Composite Pattern) 介绍将对象组合成树形结构以表示"部分-整体"的层次结构.它使得客户对单个对象和复合对象的使用具有一致性.示例有一个Message实体 ...

  3. 乐在其中设计模式(C#) - 组合模式(Composite Pattern)

    原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:weba ...

  4. 【设计模式】组合模式 Composite Pattern

    树形结构是软件行业很常见的一种结构,几乎随处可见,  比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承 ...

  5. 设计模式 - 组合模式(composite pattern) 迭代器(iterator) 具体解释

    组合模式(composite pattern) 迭代器(iterator) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考组合模式(composit ...

  6. 设计模式系列之组合模式(Composite Pattern)——树形结构的处理

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  7. python 设计模式之组合模式Composite Pattern

    #引入一 文件夹对我们来说很熟悉,文件夹里面可以包含文件夹,也可以包含文件. 那么文件夹是个容器,文件夹里面的文件夹也是个容器,文件夹里面的文件是对象. 这是一个树形结构 咱们生活工作中常用的一种结构 ...

  8. C#设计模式——组合模式(Composite Pattern)

    一.概述 在软件开发中,我们往往会遇上类似树形结构的对象体系.即某一对象既可能在树形结构中作为叶节点存在,也可能作为分支节点存在.比如在文件系统中,文件是作为叶节点存在,而文件夹就是分支节点.在设计这 ...

  9. 设计模式 -- 组合模式 (Composite Pattern)

    定义: 对象组合成部分整体结构,单个对象和组合对象具有一致性. 看了下大概结构就是集团总公司和子公司那种层级结构. 角色介绍: Component :抽象根节点:其实相当去总公司,抽象子类共有的方法: ...

随机推荐

  1. 调用python-nmap实现扫描局域网存活主机

    使用环境:Raspberry 3b+ +netifaces+python-nmap+nmap 调用netifaces自动获取ip地址: def get_gateways(): return netif ...

  2. HDU 3966 Aragorn's Story 树链拋分

    一.写在前面 终于开始开坑link-cut-tree这个了,对于网上找到的大佬的前进路线,进行了一番研发,发现实际上可以实现对于树链拋分的制作.经历了若干长时间之后终于打了出来(为什么每次学什么东西都 ...

  3. 常用的一些api

    发送手机短信 // 发送短信给安全号码 SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(phon ...

  4. TCP/IP网络编程之网络编程和套接字

    网络编程和套接字 网络编程又称为套接字编程,就是编写一段程序,使得两台连网的计算机彼此之间可以交换数据.那么,这两台计算机用什么传输数据呢?首先,需要物理连接,将一台台独立的计算机通过物理线路连接在一 ...

  5. Spring表达式语言:SpEL

    1.Spring表达式语言(简称:SpEL):是一个支持运行时查询和操作对象图的强大的表达式语言. 2.语法类似于EL:SpEL使用#{...}作为定界符,所有在大括号内的字符都被认为是SpEL. 3 ...

  6. Asp.net页面生命周期详解任我行(1)-小试牛刀,编写页面代码

    前言 很久很久以前,还是我在学校的时候,我就看了传智里面视频,学习了一下Asp.net页面生命周期,当时看的时候,因为内功不够深厚,看起来很吃力,现在回头温习了一下,还是有点收获的,于是想用博客记录一 ...

  7. 不同storyboard间跳转

    小项目中用到storyboard,可以按照模块来新建多个sb. 以下是代码实现跳转实现: UIStoryboard *anSb=[UIStoryboard storyboardWithName:@&q ...

  8. chrome浏览器设置自动切换代理上网的方法

    利用shadowsocks代理软件实现FQ时,如果都走代理模式,流量肯定不够.可以利用chrome的SwitchyOmega插件实现自动根据URL来决定是否使用代理.设置如下: 1.安装Switchy ...

  9. Python+Selenium练习篇之9-清除文本方法

    在前面的基础篇的最后一篇,我们用到了输入字符和点击按钮这样的操作.用send_keys()来输入字符串到文本输入框这样的页面元素,用click()来点击页面上支持点击的元素.有时候,我们需要清除一个文 ...

  10. LeetCode——Problem3:Longest Substring Without Repeating Characters

    哎哟我天啊.这道题快折磨死我了.刚开始连题都没看明白,就是不知道substring是什么意思.研究了好长时间才看出来的. 光辉历史呀...菜死了 1.题目 Given a string, find t ...