一、组合模式定义
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.使设计变得更加抽象