定义
将对象组合成树形结构以表示“整体-部分”的层次结构 组合模式使客户端对单个对象和组合对象保持一致的方式处理
适用场景
希望客户端可以忽略组合对象与单个对象的差异时
处理一个树形结构时
优点和缺点 优点
清楚地定义分层次的复杂对象,表示对象的全部或部分层次
让客户端忽略了层次的差异,方便对整个层次结构进行控制
简化客户端代码
符合开闭原则
缺点
结构 主要角色:抽象构件角色 : 它的主要作用是为树叶构件和树枝构件声明公共接口,并实现他们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理之类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)树叶构件角色 : 是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。树枝构件角色 : 是组合中的分支节点对象,它有子节点,用户继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含add()
、remove()
、getChild()
等方法。
代码实现 假设我们现在有一个文件夹,里面有若干个文件,我们现在需要用对象来存储,需要表示清楚其中的层级关系 首先创建抽象构件角色,根据业务情况,来控制方法是否是必须需要重写的,也可以使用接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public abstract class FileComponent { protected void add (FileComponent fileComponent) { throw new UnsupportedOperationException("无权操作添加方法" ); } protected void remove (FileComponent fileComponent) { throw new UnsupportedOperationException("无权操作删除方法" ); } protected String getName () { throw new UnsupportedOperationException("无权操作获取名称方法" ); } protected Integer getSize () { throw new UnsupportedOperationException("无权操获取大小方法除方法" ); } protected abstract void print () ; }
创建树叶构件角色,文件类,属于叶子节点,不存在删除或者添加的方法,所以我们只需要实现获取名称和大小的方法即可,另外文件本身是拥有名称和大小属性的,也可以根据实际情况将属性上提
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class FileDoc extends FileComponent { private String name; private Integer size; public FileDoc (String name, Integer size) { this .name = name; this .size = size; } @Override public String getName () { return this .name; } @Override public Integer getSize () { return this .size; } @Override protected void print () { System.out.println("文件的名称是:" + this .name + " 文件的大小是:" + this .size); } }
树枝构件角色,文件夹类,可以存放或者删除文件,拥有名称和层级属性,文件夹本身是没有大小的,但是可以依托下面文件的大小来计算大小,所以文件夹类可以实现所有的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class FolderDoc extends FileComponent { private final List<FileComponent> components = new ArrayList<>(); private String name; private Integer level; public FolderDoc (String name, Integer level) { this .name = name; this .level = level; } @Override protected String getName () { return this .name; } @Override protected Integer getSize () { return components.stream().mapToInt(FileComponent::getSize).sum(); } @Override protected void add (FileComponent fileComponent) { components.add(fileComponent); } @Override protected void remove (FileComponent fileComponent) { components.remove(fileComponent); } @Override protected void print () { System.out.println(this .name + " size=" + getSize()); for (FileComponent component : components) { if (this .level != null ){ for (int i = 0 ; i < this .level; i++) { System.out.print(" " ); } } component.print(); } } }
具体调用,我们在视频
文件夹下存放了两部电影,以及一个火影忍者
文件夹,文件夹下又有三集电视剧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Test { public static void main (String[] args) { FileComponent fileComponent = new FileDoc("倩女幽魂" ,400 ); FileComponent fileComponent1 = new FileDoc("僵尸先生" ,300 ); FileComponent fileComponent2 = new FolderDoc("火影忍者" ,2 ); FileComponent fileComponent3 = new FileDoc("火影忍者第一集" ,45 ); FileComponent fileComponent4 = new FileDoc("火影忍者第二集" ,56 ); FileComponent fileComponent5 = new FileDoc("火影忍者第三集" ,47 ); fileComponent2.add(fileComponent3); fileComponent2.add(fileComponent4); fileComponent2.add(fileComponent5); FileComponent folder = new FolderDoc("视频" ,1 ); folder.add(fileComponent); folder.add(fileComponent1); folder.add(fileComponent2); folder.print(); } }
输出结果
1 2 3 4 5 6 7 视频 size=848 文件的名称是: 倩女幽魂 文件的大小是: 400 文件的名称是: 僵尸先生 文件的大小是: 300 火影忍者 size=148 文件的名称是: 火影忍者第一集 文件的大小是: 45 文件的名称是: 火影忍者第二集 文件的大小是: 56 文件的名称是: 火影忍者第三集 文件的大小是: 47
UML类图