一. 定义与类型
定义:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使客户端对单个对象和组合对象保持一致的方式处理
类型:结构性
假设有一个树形结构的菜单,而在菜单中还可能有子菜单,子菜单下还可能有子菜单,子菜单下还有文件等等, 这种情况下可以使用组合模式。
二. 使用场景
(1) 希望客户端可以忽略组合对象与单个对象的差异
(2) 处理一个树形结构
三. 优缺点
优点:
(1) 清楚的定义层次的复杂对象,表示对象的全部或部分层次
(2) 让客户端忽略了层次的差异,方便对整个层次结构进行控制
(3) 简化客户端代码
(4) 符合开闭原则
缺点:
(1) 限制类型时会较为复杂
(2) 使设计变得更加抽象
四. 相关设计模式
组合模式和访问者模式
可以使用访问者模式来访问组合模式的递归结构
五. Coding
以课程目录与课程为例,课程目录有目录名称,课程有名称与价格,它们不是同一类实体,但是它们可以组合成整体一套课程。
创建一个目录组件类:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 11:36 **/ 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("不支持打印操作"); } }
创建一个课程类,继承组件类:
/** * @program: designModel * @description: 课程类 * @author: YuKai Fan * @create: 2019-02-12 11:39 **/ 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); } }
在创建一个课程目录类,也继承组件类:
/** * @program: designModel * @description: 课程目录类 * @author: YuKai Fan * @create: 2019-02-12 11:41 **/ public class CourseCatalog extends CatalogComponent{ private List<CatalogComponent> items = new ArrayList<CatalogComponent>(); private String name;public CourseCatalog(String name) { this.name = name; } @Override public void add(CatalogComponent catalogComponent) { items.add(catalogComponent); } @Override public void remove(CatalogComponent catalogComponent) { items.remove(catalogComponent); } @Override public void print() { System.out.println(this.name); for (CatalogComponent catalogComponent : items) { System.out.print(" "); } catalogComponent.print(); } } }
应用层:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 11:47 **/ public class Test { public static void main(String[] args) { CatalogComponent linuxCourse = new Course("Linux课程", 11); CatalogComponent windowsCourse = new Course("Windows课程", 15); CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程"); CatalogComponent mmallCourse1 = new Course("Java电商一期", 55); CatalogComponent mmallCourse2 = new Course("Java电商二期", 66); CatalogComponent designPattern = new Course("Java设计模式", 77); javaCourseCatalog.add(mmallCourse1); javaCourseCatalog.add(mmallCourse2); javaCourseCatalog.add(designPattern); CatalogComponent mainCourseCatalog = new CourseCatalog("课程主目录"); mainCourseCatalog.add(linuxCourse); mainCourseCatalog.add(windowsCourse); mainCourseCatalog.add(javaCourseCatalog); mainCourseCatalog.print(); } }
结果:
课程主目录 Course Name:Linux课程Price:11.0 Course Name:Windows课程Price:15.0 Java课程 Course Name:Java电商一期Price:55.0 Course Name:Java电商二期Price:66.0 Course Name:Java设计模式Price:77.0 Process finished with exit code 0
从上面的结果可以看出,java课程与课程主目录都属于目录,只不过等级不同,所以需要根据等级来动态的判断。例如一级目录,二级目录等等。
这些事组合模式的缺点,限制类型时会比较复杂。所以将上面代码进行改进。
课程目录类:
/** * @program: designModel * @description: 课程目录类 * @author: YuKai Fan * @create: 2019-02-12 11:41 **/ 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 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(); } } }
应用层:
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2019-02-12 11:47 **/ public class Test { public static void main(String[] args) { CatalogComponent linuxCourse = new Course("Linux课程", 11); CatalogComponent windowsCourse = new Course("Windows课程", 15); CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程",2); CatalogComponent mmallCourse1 = new Course("Java电商一期", 55); CatalogComponent mmallCourse2 = new Course("Java电商二期", 66); CatalogComponent designPattern = new Course("Java设计模式", 77); javaCourseCatalog.add(mmallCourse1); javaCourseCatalog.add(mmallCourse2); javaCourseCatalog.add(designPattern); CatalogComponent mainCourseCatalog = new CourseCatalog("课程主目录",1); mainCourseCatalog.add(linuxCourse); mainCourseCatalog.add(windowsCourse); mainCourseCatalog.add(javaCourseCatalog); mainCourseCatalog.print(); } }
结果:
UML类图:
组合模式将多个对象组合成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(叶子对象)和组合对象(容器对象)的使用具有一致性。
六. 源码分析
(1)jdk
List中的ArrayList中的addAll()方法,以及HashMap中的putAll方法都是通过继承方式的组合模式体现
(2)mybatis
SqlNode接口,该接口有很多的实现类,都是通过组合模式将多个sqlNode结合到一起(有的是组合关系,有的不是)。