合成模式属于对象的结构模式,有时又叫做“部分——整体”模式。合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式可以使客户端将单纯元素与复合元素同等看待。


合成模式

  合成模式把部分和整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由它们复合而成的合成对象同等看待。

  比如,一个文件系统就是一个典型的合成模式系统。下图是常见的计算机XP文件系统的一部分。

  从上图可以看出,文件系统是一个树结构,树上长有节点。树的节点有两种,一种是树枝节点,即目录,有内部树结构,在图中涂有颜色;另一种是文件,即树叶节点,没有内部树结构。

  显然,可以把目录和文件当做同一种对象同等对待和处理,这也就是合成模式的应用。

  合成模式可以不提供父对象的管理方法,但是合成模式必须在合适的地方提供子对象的管理方法,诸如:add()、remove()、以及getChild()等。

  合成模式的实现根据所实现接口的区别分为两种形式,分别称为安全式透明式

安全式合成模式的结构

  安全模式的合成模式要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件类中。

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

  ●  抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。合成对象通常把它所包含的子对象当做类型为Component的对象。在安全式的合成模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝构件对象给出。

  ●  树叶构件(Leaf)角色:树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。

  ●  树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝构件类给出所有的管理子对象的方法,如add()、remove()以及getChild()。

源代码

 抽象构件角色类

  1. public interface Component {
  2. /**
  3. * 输出组建自身的名称
  4. */
  5. public void printStruct(String preStr);
  6. }

树枝构件角色类

  1. public class Composite implements Component {
  2. /**
  3. * 用来存储组合对象中包含的子组件对象
  4. */
  5. private List<Component> childComponents = new ArrayList<Component>();
  6. /**
  7. * 组合对象的名字
  8. */
  9. private String name;
  10. /**
  11. * 构造方法,传入组合对象的名字
  12. * @param name 组合对象的名字
  13. */
  14. public Composite(String name){
  15. this.name = name;
  16. }
  17. /**
  18. * 聚集管理方法,增加一个子构件对象
  19. * @param child 子构件对象
  20. */
  21. public void addChild(Component child){
  22. childComponents.add(child);
  23. }
  24. /**
  25. * 聚集管理方法,删除一个子构件对象
  26. * @param index 子构件对象的下标
  27. */
  28. public void removeChild(int index){
  29. childComponents.remove(index);
  30. }
  31. /**
  32. * 聚集管理方法,返回所有子构件对象
  33. */
  34. public List<Component> getChild(){
  35. return childComponents;
  36. }
  37. /**
  38. * 输出对象的自身结构
  39. * @param preStr 前缀,主要是按照层级拼接空格,实现向后缩进
  40. */
  41. @Override
  42. public void printStruct(String preStr) {
  43. // 先把自己输出
  44. System.out.println(preStr + "+" + this.name);
  45. //如果还包含有子组件,那么就输出这些子组件对象
  46. if(this.childComponents != null){
  47. //添加两个空格,表示向后缩进两个空格
  48. preStr += " ";
  49. //输出当前对象的子对象
  50. for(Component c : childComponents){
  51. //递归输出每个子对象
  52. c.printStruct(preStr);
  53. }
  54. }
  55.  
  56. }
  57.  
  58. }

树叶构件角色类

  1. public class Leaf implements Component {
  2. /**
  3. * 叶子对象的名字
  4. */
  5. private String name;
  6. /**
  7. * 构造方法,传入叶子对象的名称
  8. * @param name 叶子对象的名字
  9. */
  10. public Leaf(String name){
  11. this.name = name;
  12. }
  13. /**
  14. * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
  15. * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
  16. */
  17. @Override
  18. public void printStruct(String preStr) {
  19. // TODO Auto-generated method stub
  20. System.out.println(preStr + "-" + name);
  21. }
  22.  
  23. }

客户端类

  1. public class Client {
  2. public static void main(String[]args){
  3. Composite root = new Composite("服装");
  4. Composite c1 = new Composite("男装");
  5. Composite c2 = new Composite("女装");
  6.  
  7. Leaf leaf1 = new Leaf("衬衫");
  8. Leaf leaf2 = new Leaf("夹克");
  9. Leaf leaf3 = new Leaf("裙子");
  10. Leaf leaf4 = new Leaf("套装");
  11.  
  12. root.addChild(c1);
  13. root.addChild(c2);
  14. c1.addChild(leaf1);
  15. c1.addChild(leaf2);
  16. c2.addChild(leaf3);
  17. c2.addChild(leaf4);
  18.  
  19. root.printStruct("");
  20. }
  21. }

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

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

透明式合成模式的结构

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

  

源代码

  抽象构件角色类

  1. public abstract class Component {
  2. /**
  3. * 输出组建自身的名称
  4. */
  5. public abstract void printStruct(String preStr);
  6. /**
  7. * 聚集管理方法,增加一个子构件对象
  8. * @param child 子构件对象
  9. */
  10. public void addChild(Component child){
  11. /**
  12. * 缺省实现,抛出异常,因为叶子对象没有此功能
  13. * 或者子组件没有实现这个功能
  14. */
  15. throw new UnsupportedOperationException("对象不支持此功能");
  16. }
  17. /**
  18. * 聚集管理方法,删除一个子构件对象
  19. * @param index 子构件对象的下标
  20. */
  21. public void removeChild(int index){
  22. /**
  23. * 缺省实现,抛出异常,因为叶子对象没有此功能
  24. * 或者子组件没有实现这个功能
  25. */
  26. throw new UnsupportedOperationException("对象不支持此功能");
  27. }
  28.  
  29. /**
  30. * 聚集管理方法,返回所有子构件对象
  31. */
  32. public List<Component> getChild(){
  33. /**
  34. * 缺省实现,抛出异常,因为叶子对象没有此功能
  35. * 或者子组件没有实现这个功能
  36. */
  37. throw new UnsupportedOperationException("对象不支持此功能");
  38. }
  39. }

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

  1. public class Composite extends Component {
  2. /**
  3. * 用来存储组合对象中包含的子组件对象
  4. */
  5. private List<Component> childComponents = new ArrayList<Component>();
  6. /**
  7. * 组合对象的名字
  8. */
  9. private String name;
  10. /**
  11. * 构造方法,传入组合对象的名字
  12. * @param name 组合对象的名字
  13. */
  14. public Composite(String name){
  15. this.name = name;
  16. }
  17. /**
  18. * 聚集管理方法,增加一个子构件对象
  19. * @param child 子构件对象
  20. */
  21. public void addChild(Component child){
  22. childComponents.add(child);
  23. }
  24. /**
  25. * 聚集管理方法,删除一个子构件对象
  26. * @param index 子构件对象的下标
  27. */
  28. public void removeChild(int index){
  29. childComponents.remove(index);
  30. }
  31. /**
  32. * 聚集管理方法,返回所有子构件对象
  33. */
  34. public List<Component> getChild(){
  35. return childComponents;
  36. }
  37. /**
  38. * 输出对象的自身结构
  39. * @param preStr 前缀,主要是按照层级拼接空格,实现向后缩进
  40. */
  41. @Override
  42. public void printStruct(String preStr) {
  43. // 先把自己输出
  44. System.out.println(preStr + "+" + this.name);
  45. //如果还包含有子组件,那么就输出这些子组件对象
  46. if(this.childComponents != null){
  47. //添加两个空格,表示向后缩进两个空格
  48. preStr += " ";
  49. //输出当前对象的子对象
  50. for(Component c : childComponents){
  51. //递归输出每个子对象
  52. c.printStruct(preStr);
  53. }
  54. }
  55.  
  56. }
  57.  
  58. }

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

  1. public class Leaf extends Component {
  2. /**
  3. * 叶子对象的名字
  4. */
  5. private String name;
  6. /**
  7. * 构造方法,传入叶子对象的名称
  8. * @param name 叶子对象的名字
  9. */
  10. public Leaf(String name){
  11. this.name = name;
  12. }
  13. /**
  14. * 输出叶子对象的结构,叶子对象没有子对象,也就是输出叶子对象的名字
  15. * @param preStr 前缀,主要是按照层级拼接的空格,实现向后缩进
  16. */
  17. @Override
  18. public void printStruct(String preStr) {
  19. // TODO Auto-generated method stub
  20. System.out.println(preStr + "-" + name);
  21. }
  22.  
  23. }

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

  1. public class Client {
  2. public static void main(String[]args){
  3. Component root = new Composite("服装");
  4. Component c1 = new Composite("男装");
  5. Component c2 = new Composite("女装");
  6.  
  7. Component leaf1 = new Leaf("衬衫");
  8. Component leaf2 = new Leaf("夹克");
  9. Component leaf3 = new Leaf("裙子");
  10. Component leaf4 = new Leaf("套装");
  11.  
  12. root.addChild(c1);
  13. root.addChild(c2);
  14. c1.addChild(leaf1);
  15. c1.addChild(leaf2);
  16. c2.addChild(leaf3);
  17. c2.addChild(leaf4);
  18.  
  19. root.printStruct("");
  20. }
  21. }

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

两种实现方法的选择

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

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

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

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

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

java设计模式---合成模式2的更多相关文章

  1. Java设计模式-合成模式

    合成模式有时也叫组合模式,对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性.掌握组合模式的重点是要理解清楚 "部分/ ...

  2. java设计模式---合成模式3

    实例 下面以一个逻辑树为例子,以上面的原理图为蓝本,看看如何实现并如何使用这个树,这个结构很简单,但是如何去使用树,遍历树.为我所用还是有一定难度的.   这里主要用到树的递归遍历,如何递归.如何控制 ...

  3. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

  4. java设计模式--单列模式

    java设计模式--单列模式 单列模式定义:确保一个类只有一个实例,并提供一个全局访问点. 下面是几种实现单列模式的Demo,每个Demo都有自己的优缺点: Demo1: /** * 单列模式需要满足 ...

  5. 3.java设计模式-建造者模式

    Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...

  6. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  7. Java设计模式——外观模式

    JAVA 设计模式 外观模式 用途 外观模式 (Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式是一种结构型模式. 结构

  8. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  9. 【设计模式】Java设计模式 - 原型模式

    [设计模式]Java设计模式 - 原型模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

随机推荐

  1. struct2利用相关的Aware接口

    Struts 2提供了Aware接口.Aware为"感知"的意思,实现了相关Aware接口的Action能够感知相应的资源.Struts在实例化一个Action实例时,如果发现它实 ...

  2. 在Spring Boot中使用Spring Security实现权限控制

    丢代码地址 https://gitee.com/a247292980/spring-security 再丢pom.xml <properties> <project.build.so ...

  3. vue开发中遇到的问题集锦(2)

    1,在搭建了一个vue的脚手架之后,写了第一个组件,路由也已经配置完毕,且页面的路由显示是:http://localhost:8080/#/userLogin,userLogin里面有内容,但是页面显 ...

  4. android 欢迎界面的制作

    再打开手机app的时候,最先映入我们眼帘的是一个覆盖手机全屏的欢迎界面,在这个界面显示出来的时候整个手机屏幕只会显示这一个界面,上面的标题栏,以及手机最顶端的状态栏都会消失,只有欢迎页面结束跳转到其他 ...

  5. [HCNA]VLAN配置Access接口

    实验目的 1.理解VLAN的应用场景 2.掌握VLAN的基本配置 3.掌握Access接口的配置方法 4.掌握Access接口加入相应VLAN的方法 实验仪器 eNSP 实验原理 如网络拓扑图所示 各 ...

  6. dict的操作和三级菜单

    dict的基本操作 # Author:nadech info = { "stu001":"sjp", "stu002":"cxx& ...

  7. Swift中String和NSString的一个不同之处

    我们知道在Swift中String和NSString是可以互相转换使用的-额-应该是在绝大数情况下可以互相转换使用.在某些情况下可能还有一丝丝略微的差别:比如在涉及到处理字符串中字符索引的时候. 我们 ...

  8. Android图表库MPAndroidChart(十四)——在ListView种使用相同的图表

    Android图表库MPAndroidChart(十四)--在ListView种使用相同的图表 各位好久不见,最近挺忙的,所有博客更新的比较少,这里今天说个比较简单的图表,那就是在ListView中使 ...

  9. windows curl命令详解

    概述 Curl命令可以通过命令行的方式,执行Http请求.在Elasticsearch中有使用的场景,因此这里研究下如何在windows下执行curl命令. 软件下载 下载地址:https://cur ...

  10. DOS界面下的翻译软件制作

    准备 素材 依赖 接口 地址 参数 返回值解析 编码及测试 功能代码 运行脚本 环境变量 结果展示 英语转汉语 汉语转英语 总结 昨天看到一篇关于Linux下的桌面词典的文章,于是就想实现一个Wind ...