介绍
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
其主要的作用是将一组相似对象可以以相同的接口进行操作,这样可以使客户端保持一致的处理这些对象,并且可以增加新对象的同时避免修改客户端代码,符合开闭原则。
组合模式结构
组合模式因为是树形结构,主要由三种结构组成
- 抽象构件(Component)角色:树的根节点,它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。
- 树枝构件(Composite)角色 :树的分支节点,它有子节点,需要继承及实现抽象构件的函数。它的主要作用是存储和管理子部件,因此通常包含 Add()、Remove()、GetChild() 等方法。
- 树叶构件(Leaf)角色:是组合中树的叶子节点对象,它没有子节点,用于继承或实现抽象构件,通常不包含Add()、Remove()、GetChild() 等方法。
案例
以大学为例,大学的组织结构简单化处理可能有以下几个部分,分别为学校,学院,年级,班级,按照常规的逻辑来思考,这是一个包含的关系,但是每个部分都可能会有自己的相关功能,如果我们将所有的部分都视为不同的对象,那么假如此时我们需要一个统筹管理整个学校各个部分的软件,则每个部分我们都需要调用对应的接口来管理。但是其实像一些内部的增删改查等,这几个部分的管理都是需要的,所以我们可以用组合模式将这几部分看作相同对象,提供相同的接口完成一些工作。
具体实现
抽象构件
定义公共方法
public interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void showAllChild();
}
树枝构件
大学类
@Data
//大学类
public class University implements Component{
String name;
List<Component> componentList=new ArrayList<Component>();
@Override
public void add(Component c) {
componentList.add(c);
}
public University(String name){
this.name=name;
}
@Override
public void remove(Component c) {
componentList.remove(c);
}
@Override
public Component getChild(int i) {
if(i<componentList.size()){
return componentList.get(i);
}
return null;
}
@Override
public void showAllChild() {
System.out.println("访问"+name);
//递归访问所有子节点
for (Object obj:componentList){
((Component) obj).showAllChild();
}
}
}
学院类
//学院类
@Data
public class College implements Component{
String name;
List<Component> componentList=new ArrayList<Component>();
//需要注意,并不是学校和学院的这些接口实现必须一致,相反,两者可能存在功能上的较大差异,完全可以是不同的实现,只是保持相同的对外接口
@Override
public void add(Component c) {
componentList.add(c);
}
public College(String name){
this.name=name;
}
@Override
public void remove(Component c) {
componentList.remove(c);
}
@Override
public Component getChild(int i) {
if(i<componentList.size()){
return componentList.get(i);
}
return null;
}
@Override
public void showAllChild() {
System.out.println("访问"+name);
//递归访问所有子节点
for (Object obj:componentList){
((Component) obj).showAllChild();
}
}
}
树叶构件
年级类
@Data
//我这里代码简化了,把年级设为叶子节点,实际上年级下面还可能有层级
public class Grade implements Component{
String name;
//叶节点没有子节点,add,remove getChild不需要实现
@Override
public void add(Component c) {
}
public Grade(String name){
this.name=name;
}
@Override
public void remove(Component c) {
}
@Override
public Component getChild(int i) {
return null;
}
@Override
public void showAllChild() {
System.out.println("访问"+name);
}
}
测试
@Test
public void test()
{
//创建组织结构
Component university=new University("XX大学");
Component college1=new College("XX学院");
Component college2=new College("YY学院");
Component grade1=new Grade("XX学院XX级");
Component grade2=new Grade("XX学院YY级");
Component grade3=new Grade("YY学院XX级");
Component grade4=new Grade("YY学院YY级");
college1.add(grade1);
college1.add(grade2);
college2.add(grade3);
college2.add(grade4);
university.add(college1);
university.add(college2);
university.showAllChild();
}
测试结果
访问XX大学
访问XX学院
访问XX学院XX级
访问XX学院YY级
访问YY学院
访问YY学院XX级
访问YY学院YY级
总结
由上面案例可以看出,组合模式可以简化客户端操作,客户端只需要调用相同的接口就可以操作不同的对象,并且当新增加对象是客户端不需要修改代码。
同时可以看出在需要表示一个对象整体与部分的层次结构的场合或者遍历组织架构,或者处理的对象具有树形结构时非常适合组合模式。