有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。
效果:
1.定义了包含基本对象和组合对象的类层次结构:
基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到 基本对象的地方都可以使用组合对象。
2.简化客户代码:
客户可以一致地使用组合结构和单个对象。通常用户不知道 (也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码 , 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
3.使得更容易增加新类型的组件:
新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component类而改变。
4.使你的设计变得更加一般化:
容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
涉及角色:
1.Component 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
2.Leaf 在组合中表示叶子结点对象,叶子结点没有子结点。
3.Composite 定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
类图:
ER图:
代码示例:
假设现在需要设计一套角色系统,包括组长和组员,组员又可以是其他组的组长。组长可以新增、删除组员,同时还必须知道下面所有的组员名字。
public abstract class Role { private String name; public Role(){} public Role(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract void add(Role role); public abstract void remove(Role role); public abstract void display(); public abstract Role getChild(int i); }
public class TeamLeader extends Role { private List<Role> list; public TeamLeader(){ list = new ArrayList<Role>(); } public TeamLeader(String name){ super(name); list = new ArrayList<Role>(); } @Override public void add(Role role) { list.add(role); } @Override public void remove(Role role) { list.remove(role); } @Override public void display() { System.out.println(this.getName()); for (Role p : list){ p.display(); } } @Override public Role getChild(int i) { System.out.println(this.getName()+"的第"+(i+1)+"个组员是"+list.get(i).getName()); return list.get(i); } }
public class Member extends Role { public Member(){}; public Member(String name){ super(name); } @Override public void add(Role role) { System.out.println(this.getName()+"不能够添加组员"); } @Override public void remove(Role role) { System.out.println(this.getName()+"不能够删除组员"); } @Override public void display() { System.out.println(this.getName()); } @Override public Role getChild(int i) { System.out.println(this.getName()+"没有组员"); return null; } }测试方法:
public static void main(String[] args) { Role p1 = new TeamLeader("张组长"); Role p2 = new TeamLeader("陈组长"); Role p3 = new Member("组员a"); Role p4 = new Member("组员b"); Role p5 = new Member("组员c"); Role p6 = new Member("组员d"); Role p7 = new Member("组员e"); p1.add(p2); p1.add(p3); p1.add(p4); p1.add(p5); p2.add(p6); p2.add(p7); p1.display(); p2.getChild(1); p5.getChild(1); }
执行结果:
张组长
陈组长
组员d
组员e
组员a
组员b
组员c
陈组长的第2个组员是组员e
组员c没有组员
结果分析:
这个案例中,组长-组员就组成了一个树形结构。p1.display()方法将组长p1下的各级组员的名字都显示了出来,对于组长或者组员来说,都实现了display这个方法,显示名称。这样对于客户端来说,处理组长和处理组员就是一样的操作了。这也就是组合模式希望的结果。在组长和组员中实现角色接口中的抽象方法,各自完成自身的逻辑。
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以像处理简单元素一样来处理复杂元素。
如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。例如文件系统中,文件与目录组成了一个树形结构;网站导航中各级目录结果也是一个树形结构。类似这样的树形结构,都可以考虑使用组合模式。