1 基础知识
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。特征:组合模式使得客户端对单个对象和组合对象保持一致的方式处理。
本质:统一叶子对象和组合对象。
目的:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。
使用场景:希望客户端可以忽略组合对象与单个对象的差异;处理一个树形结构时。
优点:清楚定义分层次的复杂对象,表示对象的全部或部分层次;让客户端忽略了层次的差异,方便对整个层次结构控制;更容易扩展;符合开闭原则。缺点:限制类型时会比较复杂;使设计变得更加抽象。
2 代码示例:
使用场景:以慕课网为例,有一个Java课程目录,在这目录下有多种Java课程,如果把具体的Java课程继承课程目录那么就可以视课程目录与具体课程为同一对象操作。
对于课程目录可以的显示方式如下:利用空格缩进表示主目录和子目录
抽象目录组件:CatalogComponent
public abstract class CatalogComponent { public void add(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持添加操作"); } public void remove(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持删除操作"); } public String getName(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持获取名称操作"); } public double getPrice(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持获取价格操作"); } public void print(){ throw new UnsupportedOperationException("不支持打印操作"); } }
课程类:Course
public class Course extends CatalogComponent { //课程名称 private String name; //课程价格 private double price; public Course(String name, double price) { this.name = name; this.price = price; } @Override public String getName(CatalogComponent catalogComponent) { return this.name; } @Override public double getPrice(CatalogComponent catalogComponent) { return this.price; } @Override public void print() { System.out.println("Course Name:"+name+" Price:"+price); } }
课程目录:CourseCatalog
public class CourseCatalog extends CatalogComponent { //在课程目录中会有许多课程,这些课程又属于目录组件 private List<CatalogComponent> items = new ArrayList<CatalogComponent>(); private String name; //为了使输出的排版更清晰 private Integer level; public CourseCatalog(String name,Integer level) { this.name = name; this.level = level; } @Override public void add(CatalogComponent catalogComponent) { items.add(catalogComponent); } @Override public String getName(CatalogComponent catalogComponent) { return this.name; } @Override public void remove(CatalogComponent catalogComponent) { items.remove(catalogComponent); } @Override public void print() { System.out.println(this.name); for(CatalogComponent catalogComponent : items){ if(this.level != null){ for(int i = 0; i < this.level; i++){ System.out.print(" "); } } catalogComponent.print(); } } }
应用层:
public class Test { public static void main(String[] args) { CatalogComponent linuxCourse = new Course("Linux课程",11); CatalogComponent windowsCourse = new Course("Windows课程",11); //Java课程目录为2级目录 CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录",2); CatalogComponent mmallCourse1 = new Course("Java电商一期",55); CatalogComponent mmallCourse2 = new Course("Java电商二期",66); CatalogComponent designPattern = new Course("Java设计模式",77); //把课程添加到java课程目录中 javaCourseCatalog.add(mmallCourse1); javaCourseCatalog.add(mmallCourse2); javaCourseCatalog.add(designPattern); //网站课程主目录级别为1 CatalogComponent imoocMainCourseCatalog = new CourseCatalog("慕课网课程主目录",1); imoocMainCourseCatalog.add(linuxCourse); imoocMainCourseCatalog.add(windowsCourse); imoocMainCourseCatalog.add(javaCourseCatalog); imoocMainCourseCatalog.print(); } }
输出效果:
类关系图如下图: 星号表示多个
3 相关模式
(1)组合模式和装饰模式
这两个模式可以组合使用。装饰模式在组装多个装饰器对象的时候,是一个装饰器找下一个装饰器,下一个再找下一个,如此递归下去。其实这种结构也可以使用组合模式来帮助构建,这样一来,装饰器对象就相当于组合模式的 Composite X对象了。要让两个模式能很好地组合使用,通常会让它们有一个公共的父类。因此装饰器必须支持组合模式需要的一些功能,比如,增加、删除子组件等。
(2)组合模式和享元模式
这两个模式可以组合使用。 如果组合模式中出现大量相似的组件对象的话,可以考虑使用享元模式来帮助级存组件对象,这样可以减少对内存的需要。使用享元模式也是有条件的,如果组件对象的可变化部分的状态能够从组件对象中分离出去,并且组件对象本身不需要向父组件发送请求的话,就可以采用享元模式。
(3)组合模式和迭代器模式
这两个模式可以组合使用。在组合模式中,通常可以使用迭代器模式来遍历组合对象的子对象集合,而无需关心具体存放子对象的聚合结构。
(4)组合模式和访问者模式
这两个模式可以组合使用。访问者模式能够在不修改原有对象结构的情况下,为对象结构中的对象增添新的功能。访问者模式和组合模式合用,可以把原本分教在 Composite和Laf类中的操作和行为都局部化。如果在使用组合模式的时候,预计到今后可能会有增添其他功能的可能,那么可以采用访问者模式,来预留好添加新功能的方式和通道,这样以后在添加新功能的时候,就不需要再修改已有的对象结构和已经实现的功能了。
(5)组合模式和职责链模式
这两个模式可以组合使用。职责链模式要解决的问题是:实现请求的发送者和接收者之间解耦。职责链模式的实现方式是把多个接收者组合起来,构成职责链,然后让请求在这条上传递,直到有接收者处理这个请求为止可以应用组合模式来构建这条链,相当于是子组件找父组件,父组件又找父组件,如此递归下去,构成一条处理请求的组件对象链。
(6)组合模式和命令模式
这两个模式可以组合使用。命令模式中有一个宏命令的功能,通常这个宏命令就是使用组合模式来组装出来的。
0