一、组合模式定义

1.组合模式也称为整体-部分模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性,属于结构型模式

2.组合关系和聚合关系的区别

  A.组合关系:具有相同的生命周期

  B.聚合关系:具有不同的生命周期

3.组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,最顶层的节点称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点

4.根节点和树枝节点本质上是同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于同一种类型,但是在组合模式中,会把树枝节点和叶子节点认为是同一种数据类型(用同一接口定义),让它们具备一致行为。这样,在组合模式中,整个树形结构中的对象都是同一种类型,带来的一个好处就是客户无需辨别树枝节点还是叶子节点,而是可以直接进行操作,给客户使用带来极大便利

5.组合模式应用场景:

  A.希望客户端可以忽略组合对象与单个对象的差异时

  B.对象层次具备整体和部分,呈树形结构

 

二、组合模式示例

1.组合模式中包含三个角色:

  A.抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性

  B.树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构

  C.叶子节点(leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位

 

2.组合模式在代码具体实现上,有两种不同的方式:透明组合模式和安全组合模式

 

3.透明组合模式

  1 public abstract class CourseComponent {
  2     public void addChild(CourseComponent catalogComponent){
  3         throw new UnsupportedOperationException("不支持添加操作");
  4     }
  5 
  6     public void removeChild(CourseComponent catalogComponent){
  7         throw new UnsupportedOperationException("不支持删除操作");
  8     }
  9 
 10     public String getName(CourseComponent catalogComponent){
 11         throw new UnsupportedOperationException("不支持获取名称操作");
 12     }
 13 
 14     public double getPrice(CourseComponent catalogComponent){
 15         throw new UnsupportedOperationException("不支持获取价格操作");
 16     }
 17 
 18     public void print(){
 19         throw new UnsupportedOperationException("不支持打印操作");
 20     }
 21 }
 22 
 23 public class Course extends CourseComponent {
 24     private String name;
 25     private double price;
 26 
 27     public Course(String name, double price) {
 28         this.name = name;
 29         this.price = price;
 30     }
 31 
 32     @Override
 33     public String getName(CourseComponent catalogComponent) {
 34         return this.name;
 35     }
 36 
 37     @Override
 38     public double getPrice(CourseComponent catalogComponent) {
 39         return this.price;
 40     }
 41 
 42     @Override
 43     public void print() {
 44         System.out.println(name + "(¥" + price + "元)");
 45     }
 46 }
 47 
 48 public class CoursePackage extends CourseComponent {
 49 
 50     private List<CourseComponent> items = new ArrayList<CourseComponent>();
 51     private String name;
 52     private Integer level;
 53 
 54     public CoursePackage(String name, Integer level) {
 55         this.name = name;
 56         this.level = level;
 57     }
 58 
 59     @Override
 60     public void addChild(CourseComponent catalogComponent) {
 61         items.add(catalogComponent);
 62     }
 63 
 64     @Override
 65     public String getName(CourseComponent catalogComponent) {
 66         return this.name;
 67     }
 68 
 69     @Override
 70     public void removeChild(CourseComponent catalogComponent) {
 71         items.remove(catalogComponent);
 72     }
 73 
 74     @Override
 75     public void print() {
 76         System.out.println(this.name);
 77 
 78         for(CourseComponent catalogComponent : items){
 79             if(this.level!=null){
 80                 for(int i=0;i<this.level;i++){
 81                     System.out.print(" ");
 82                 }
 83                 for(int i=0;i<this.level;i++){
 84                     if(i==0){
 85                         System.out.print("+");
 86                     }
 87                     System.out.print("-");
 88                 }
 89             }
 90             catalogComponent.print();
 91         }
 92     }
 93 }
 94 
 95 public class TransparentComponentTest {
 96     public static void main(String[] args) {
 97         System.out.println("========透明组合模式==========");
 98 
 99         CourseComponent javaBase = new Course("java入门课程", 8280);
100         CourseComponent ai = new Course("人工智能", 5000);
101 
102         CourseComponent packageCourse = new CoursePackage("java架构师课程", 2);
103 
104         CourseComponent design = new Course("java设计模式", 1500);
105         CourseComponent source = new Course("源码分析", 2000);
106         CourseComponent softSkill = new Course("软技能", 3000);
107 
108         packageCourse.addChild(design);
109         packageCourse.addChild(source);
110         packageCourse.addChild(softSkill);
111 
112         CourseComponent catalog = new CoursePackage("课程主目录", 1);
113         catalog.addChild(javaBase);
114         catalog.addChild(ai);
115         catalog.addChild(packageCourse);
116 
117         catalog.print();
118     }
119 }

透明组合模式把所有公共方法都定义在Component中,这样做的好处是客户端无需分辨是叶子节点还是树枝节点,它们具备完全一致的接口;缺点是叶子节点会继承得到一些它所不需要的方法,违背了接口隔离原则

 

4.安全组合模式

安全组合模式是只规定系统各个层次的最基础的一致行为,而把组合本身的方法放到自身当中

  1 public abstract class Directory {
  2     protected String name;
  3 
  4     public Directory(String name) {
  5         this.name = name;
  6     }
  7 
  8     public abstract void show();
  9 }
 10 
 11 public class File extends Directory {
 12 
 13     public File(String name) {
 14         super(name);
 15     }
 16 
 17     @Override
 18     public void show() {
 19         System.out.println(this.name);
 20     }
 21 }
 22 
 23 public class Folder extends Directory {
 24 
 25     private List<Directory> dirs;
 26     private Integer level;
 27 
 28     public Folder(String name, Integer level) {
 29         super(name);
 30         this.level = level;
 31         this.dirs = new ArrayList<Directory>();
 32     }
 33 
 34     @Override
 35     public void show() {
 36         System.out.println(this.name);
 37         for(Directory dir : dirs){
 38             if(this.level!=null){
 39                 for(int i=0;i<this.level;i++){
 40                     System.out.print(" ");
 41                 }
 42                 for(int i=0;i<this.level;i++){
 43                     if(i==0){
 44                         System.out.print("+");
 45                     }
 46                     System.out.print("-");
 47                 }
 48             }
 49             dir.show();
 50         }
 51     }
 52 
 53     public boolean add(Directory dir){
 54         return this.dirs.add(dir);
 55     }
 56 
 57     public boolean remove(Directory dir){
 58         return this.dirs.remove(dir);
 59     }
 60 
 61     public Directory get(int index){
 62         return this.dirs.get(index);
 63     }
 64 
 65     public void list(){
 66         for(Directory dir : this.dirs){
 67             System.out.println(dir.name);
 68         }
 69     }
 70 }
 71 
 72 public class SafeComponentTest {
 73     public static void main(String[] args) {
 74         System.out.println("=========安全组合模式==========");
 75         File qq = new File("QQ.exe");
 76         File wx = new File("微信.exe");
 77 
 78         Folder office = new Folder("办公软件", 2);
 79 
 80         File word = new File("Word.exe");
 81         File ppt = new File("PowerPoint.exe");
 82         File excel = new File("Excel.exe");
 83 
 84         office.add(word);
 85         office.add(ppt);
 86         office.add(excel);
 87 
 88         Folder wps = new Folder("金山软件", 3);
 89         wps.add(new File("WPS.exe"));
 90         office.add(wps);
 91 
 92         Folder root = new Folder("根目录", 1);
 93         root.add(qq);
 94         root.add(wx);
 95         root.add(office);
 96 
 97         System.out.println("====show()方法效果===");
 98         root.show();
 99 
100         System.out.println("=====list()方法效果====");
101         root.list();
102     }
103 }

安全组合模式的好处是接口定义职责清晰,符合设计模式单一职责原则和接口隔离原则;缺点是客户端需区分树枝节点和叶子节点,这样才能处理各个层次的操作,客户端无法依赖抽象,违背了依赖倒置原则

 

5.组合模式的优缺点

  A.优点

    a.清楚地定义分层次的复杂对象,表示对象的全部或部分层次

    b.让客户端忽略了层次的差异,方便对整个层次结构进行控制

    c.简化客户端代码

  B.缺点

    a.限制类型时会较为复杂

    b.使设计变得更加抽象