一、引入
学校院系展示需求:
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个专业。
即:
----------西安工业大学----------
----------计算机科学与工程学院----------
软件工程
计算机科学与技术
物联网工程
----------电子信息工程学院----------
通信工程专业
自动化专业
1.传统方案解决学校院系展示
传统方法:先写一个学校,学校下面有学院,学院下面有专业;
把它们做成继承的关系
问题:学校与学院、学院与专业之间真的是继承关系吗?
更合适的关系是:学校包含学院,而学院包含专业,它们之间准确的关系应该是组合的关系
分析:
把学院当做学校的子类、专业当作学院的子类,这是在组织大小的角度来对其进行分层,即:小的组织结构继承大的组织结构。这样不能很好实现的管理的操作,比如对学院、专业的添加,删除,遍历等操作
解决方法:
把学校、学院、专业都看做是组织结构(他们的地位相等),他们之间没有继承的关系,而是形成一个树形结构,因而可以更好的实现管理操作。
这样就引出组合模式
二、组合模式
组合模式的目标:
组合模式描述如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象
- 对于树形结构,树形结构中有容器对象(如文件夹)、成员对象(可以是容器对象,也可以是叶子对象,如子文件夹和文件),如何通过递归调用的方式将遍历整个树形结构,从而找到想要的目标对象、对其执行调用。
- 容器对象和叶子对象在功能上是有区别的,【容器对象是往进遍历,但找到文件后不再遍历】,但对于客户端来说是没有区别的,在使用这些对象的客户端代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下客户端希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。
基本介绍:
1)定义:组合多个对象形成树形结构,以表示**“整体-部分”的结构层次**。组合模式使得用户对单个对象(即叶子对象,即不能再递归的终极节点)和组合对象(即容器对象)的使用具有一致性。
2)又称部分整体模式,属于结构型模式【主要站在软件组织结构的角度讨论如何写代码或如何设计功能模块】
3)描述部分和整体的关系【比如:学院是学校的一部分,某个专业是学院的一部分】,它创建对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
4)在实际软件开发中的使用频率较高,【在实际中通常操作的都是集合对象而非单个】
其原理类图如下:
基本结构为三角形,顶点为抽象层即Component,另两角是需要组合的部分Composite和Leaf 。把Composite当作重心,其一般会有Leaf的引用,Leaf当作要组合的部件,可以通过Component抽象的接口替换到Composite中Leaf的引用
【Composite和Lea相当于双截棍的两端,Component是连接它们的链子,从而两端较灵活,动态性】
说明:
组合模式有4种角色,分别为
- Component :抽象层,组合中对象声明接口。在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component子部件。Component可以是抽象类或者接口
- Leaf : 表示叶子节点,它的下面没有子节点。【其里面不会有相应的方法,是被管理者,它下面就不可再分了】
3)Composite:非叶子节点,它下面还管理叶子节点,充当管理者
Composite中可以定义Component的变量, Leaf 继承Component ,通过里氏代换原则可以替换Component到Composite中。Composite也继承自Component且自包含。所以Composite中 可以包含Composite和Leaf【例如:文件夹可以包含文件夹和文件】。因而它相当于容器,list集合)
4)Client:直接和Component 交互,实现无差别的对Leaf和Composite的功能的调用
组合模式分析:
1) 组合模式的关键是定义一个抽象构件类,既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。
2)容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。
2.上述学校院系展示需求用组合模式实现
1)OriganizationComponent:可以是抽象类,也可以是具体的类,也可以是接口,视情况而定,重点在于定相关的方法,但是方法由下面的子类实现;
2)Department:叶子节点,College和University是非叶子节点,University里可以包含很多College,University通过OrganizationComponent去聚合College,College通过OrganizationComponent去聚合Department,
import java.util.ArrayList;
import java.util.List;
public abstract class OrganizationComponent {
private String name; // 名字
private String des; // 描述说明
//add方法
protected void add(OrganizationComponent organizationComponent) {
// 该方法目前无法确定,用默认实现
//抛出不支持操作的异常
throw new UnsupportedOperationException();
}
//remove方法,同上
protected void remove(OrganizationComponent organizationComponent) {
// 默认实现
throw new UnsupportedOperationException();
}
// 构造器
public OrganizationComponent(String name, String des) {
super();
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
// 打印方法print,叶子节点和非叶子节点都有该方法,做成抽象的,子类都需要实现
protected abstract void print();
}
//University就是Composite,可以管理College
public class University extends OrganizationComponent {
//聚合,此处List 中存放的是College
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public University(String name, String des) {
super(name, des);
}
// 重写add
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
// 重写remove
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
// print方法,就是输出University包含的学院
protected void print() {
System.out.println("----------" + getName() + "----------");
// 遍历organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
public class College extends OrganizationComponent {
//聚合,List 中存放的Department
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public College(String name, String des) {
super(name, des);
}
// 重写add
protected void add(OrganizationComponent organizationComponent) {
// 将来实际业务中,College的add方法和University的add不一定完全相同,可以重写
organizationComponents.add(organizationComponent);
}
// 重写remove
protected void remove(OrganizationComponent organizationComponent) {
organizationComponent.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
// print方法,就是输出University包含的学院
protected void print() {
System.out.println("----------" + getName() + "----------");
// 遍历organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
public class Department extends OrganizationComponent {
// 不再管理,没有集合
//构造器
public Department(String name, String des) {
super(name, des);
}
// add,remove 就不需要写了,因为它是叶子节点,不管理其他
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
protected void print() {
System.out.println(getName());
}
}
public class Client {
public static void main(String[] args) {
// 从大到小创建对象
//创建学校
OrganizationComponent university = new University("西安工业大学", "是一所以兵工为特色的大学");
// 创建学院
OrganizationComponent computercollege = new College("计算机科学与工程学院", "计算机学院");
OrganizationComponent elecInfoEngineercollege = new College("电子信息工程学院", "电信学院");
// 创建各个学院下面的专业
computercollege.add(new Department("软件工程", "面向企业第一线培养应用型的软件工程师"));
computercollege.add(new Department("计算机科学与技术", "注重培养计算机及嵌入式系统的研究、设计、开发、应用等方面的高级工程技术人才"));
computercollege.add(new Department("物联网工程", "突出培养物联网系统规划与设计、信息感知与处理的应用开发能力"));
elecInfoEngineercollege.add(new Department("通信工程专业", "瞄准移动通信、网络通信和物联网"));
elecInfoEngineercollege.add(new Department("自动化专业", "获批为国家级特色专业、卓越工程师教育培养计划专业、陕西省专业综合改革试点专业"));
// 将学院加入到学校
university.add(computercollege);
university.add(elecInfoEngineercollege);
university.print();
// computercollege.print();
// elecInfoEngineercollege.print();
}
}
三、组合模式能够解决的问题
当要处理的对象可以生成一颗树形结构,而要对树上的节点和叶子进行操作时,组合模式能够提供一致的方式,而不用考虑它是节点还是叶子
即结构类似于下图所示的结构:
组合模式适用情况
1、当需要表示一个对象整体或部分层次时,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
2、让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。
3、 当对象的结构是动态的并且复杂程度不一样,但客户需要一致地处理它们时。
实例:文件浏览
- 文件有不同类型,不同类型的文件其浏览方式有所区别,如文本文件和图片文件的浏览方式就不相同。对文件夹的浏览实际上就是对其中所包含文件的浏览,而客户端可以一致地对文件和文件夹进行操作,无须关心它们的区别。
- 操作系统中的目录结构是一个树形结构,因此在对文件和文件夹进行操作时可以应用组合模式,例如杀毒软件在查毒或杀毒时,既可以针对一个具体文件,也可以针对一个目录。如果是对目录查毒或杀毒,将递归处理目录中的每一个子目录和文件。
组合模式的优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
- 客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。
- 更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
组合模式的缺点
- 使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。
- 增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制。