`

23种设计模式(14):合成模式

阅读更多

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

 

合成模式

 

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

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

 

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

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

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

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

安全式合成模式的结构

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

 

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

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

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

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

源代码

 抽象构件角色类

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public interface Component {  
  2.     /** 
  3.      * 输出组建自身的名称 
  4.      */  
  5.     public void printStruct(String preStr);  
  6. }  


树枝构件角色类

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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){<pre name="code" class="java">  

 


树叶构件角色类

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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. }  


客户端类

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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()等方法的声明和实现,而树叶构件类则没有给出这些方法的声明或实现。这样的做法是安全的做法,由于这个特点,客户端应用程序不可能错误地调用树叶构件的聚集方法,因为树叶构件没有这些方法,调用会导致编译错误。

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

透明式合成模式的结构

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



 

源代码

 

  抽象构件角色类

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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,其他地方无变化。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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,其他地方无变化。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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对象。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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对象,具体的类型对于客户端而言是透明的,是无须关心的。

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

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

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

分享到:
评论

相关推荐

    C++ Design Pattern 23种设计模式(全_解释+源代码).zip

    C++ 23种设计模式(全_解释+源代码).zip 23种设计模式都是基于以下原则,务必记牢 依赖倒转原则 里氏代换原则 开闭原则 接口隔离原则 合成聚合复用原则 迪米特法则 23种设计模式如下:

    C#23种设计模式_示例源代码及PDF

    合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就 合成模式 是一个处理对象的树结构的模式。 合成模式把部分与整体的关系用树结构表示出来。 合成模 式使得客户端把一个个单独的...

    设计模式(23种)与设计原则(6种)

    详细描述全部(23种)设计模式与设计原则(6种)。 (一)设计原则包括: 1、开闭原则 2、里氏代换原则 3、依赖转换原则 4、接口隔离原则 5、合成/聚合复用原则 6、最少知识原则 (二)设计模式 1)工厂模式...

    尚硅谷设计模式源码笔记课件.zip

    1) 内容包括: 设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种...

    设计模式之合成模式Java实现和类设计图

    设计模式之合成模式的Java实现版本,包括UML类设计图,包含了两种实现,安全式和透明式

    24种设计模式介绍与6大设计原则

    总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元...

    java与模式

    1:模式的简史和形而上学;2:统一建模语言UML简介;3:软件的可维护性与可复用性;4:开-闭 原则;5:java语言接口;6:抽象类;7:里氏代换原则;8:依懒倒转原则;...26:设计模式在JAVA I/O设计原则;28:代理模式;....

    C#设计模式.PDF

    设计模式(14)-Flyweight Pattern 122 一、 享元(Flyweight)模式 122 二、 单纯享元模式的结构 122 三、 单纯享元模式的示意性源代码 123 四、 复合享元模式的结构 125 五、 一个咖啡摊的例子 127 六、 咖啡屋的...

    学习JAVA-23种设计模式

    学习JAVA-23种设计模式。1.设计模式的分类2.设计模式的六大原则3.依赖倒转原则4.接口隔离原则5.迪米特法则6.合成复用原则...

    Java 23种设计模式详解

    Java 全部23种设计模式全面详解 设计模式的六大原则 1、开闭原则(Open Close Principle) 2、里氏代换原则(Liskov Substitution Principle) 3、依赖倒转原则(Dependence Inversion Principle) 4、接口隔离原则...

    C#设计模式大全

    设计模式(14)-Flyweight Pattern 一、 享元(Flyweight)模式 二、 单纯享元模式的结构 三、 单纯享元模式的示意性源代码 四、 复合享元模式的结构 五、 一个咖啡摊的例子 六、 咖啡屋的例子 七、 享元...

    《c#设计模式》电子书+随书源码

    本书内容覆盖了所有23个经典的“四人小组”设计模式,具体包括:适配器,外观,合成,桥接,单件,观察者,中介者,代理,职责链,享元,生成器,工厂方法,抽象工厂,原型,备忘录,模板方法,状态,策略,命令,...

    设计模式Demo

    针对23种设计模式,分别写了demo并画了类图帮助理解。 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰...

    [Java设计模式(第2版)(Design.Patterns.in.Java).John.Metsker

    《java设计模式(第2版)》通过一个完整的java项目对经典著作design patterns一书介绍的23种设计模式进行了深入分析与讲解,实践性强,却又不失对模式本质的探讨。本书创造性地将这些模式分为5大类别,以充分展现各个...

    Java 设计模式

    java的设计模式大体上分为三大类: 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元...

    Java23种基本的设计模式整料整理学习源码示例zip

    Java23种基本的设计模式整料整理学习源码示例zip Java23种基本的设计模式整料整理学习源码示例zip Java23种基本的设计模式整料整理学习源码示例zip Java23种基本的设计模式整料整理学习源码示例zip 设计模式的六大...

    设计模式所有实验及实验报告及代码.zip

    2GoF 的 23 种设计模式 3UML中的类图及类图之间的关系 4开闭原则 5里氏替换原则 6依赖倒置原则 7单一职责原则 8接口隔离原则 9迪米特法则 10合成复用原则 11创建型模式的特点和分类 12单例模式 13原型模式 14工厂...

    设计模式6大原则:里氏置换原则

    设计模式6大原则:里氏置换原则

    源码:阎宏设计模式光盘

    com.javapatterns.composite 合成模式 com.javapatterns.decorator 装饰模式 com.javapatterns.dip 依赖倒转原则 com.javapatterns.doubledispatch 专题:单分派与多分派 com.javapatterns.facade 门面模式 ...

Global site tag (gtag.js) - Google Analytics