设计模式——组合模式
本片博文通过尚硅谷韩老师课程所讲的《设计模式课程》做出的笔记,在此非常感谢!
基本介绍
- 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系;
- 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
- 这种类型的设计模式属于结构型模式;
- 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象;
细节和注意事项
- 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题;
- 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动;
- 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构;
- 需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式;
- 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式;
UML
问题引入
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。并且学校可以添加、删除院系,院系可以添加、删除系,打印学校的信息时会将学校和其下面的所有院系以及每个院系下的所有专业都打印出来;打印学院时会将该学院的信息以及其下所有的系都打印出来;
分析:如果我们使用传统的方式去实现这个问题,大多数人都是直接定义一个学校类
,然后让每个系类
去继承它,再让每个系类
继承对应的专业类
,这样做的优点是易于理解,但是却存在很多的问题,比如:我们在完成上面的打印学校,删除、查找某些专业时需求时,必然就会出现循环嵌套的代码;必然会导致耦合度过高,而且继承的层次太深
,不利于后续的修改和维护,因此我们对于这种情况可以考虑一下组合模式
;
编码实现
定义一个所有节点(即学校类、学院类和专业类)
都需要继承的类OrganizationComponent
,用来它们的共性,代码如下:
package edu.hebeu.combination;
/*
* 这个类相当于Component类,用来存放和抽取叶子节点和非叶子节点的共性
*/
public abstract class OrganizationComponent {
private String name; // 名字
private String desc; // 描述
public OrganizationComponent(String name, String desc) {
this.name = name;
this.desc = desc;
}
public void add(OrganizationComponent organizationComponent) {
/*默认实现该方法,因为Department类和本类是不会有add()方法的,所有在这里进行默认实现,让需要用
* 到这个方法的类去重新,不需要的就不用关注
*/
throw new UnsupportedOperationException(); // 抛出一个不支持操作的异常
}
public void remove(OrganizationComponent organizationComponent) {
/*默认实现该方法,因为Department类和本类是不会有remove()方法的,所有在这里进行默认实现,让需要
* 用到这个方法的类去重新,不需要的就不用关注
*/
throw new UnsupportedOperationException(); // 抛出一个不支持操作的异常
}
public OrganizationComponent find(String name) {
/*默认实现该方法,因为Department类和本类是不会有find()方法的,所有在这里进行默认实现,让需要
* 用到这个方法的类去重新,不需要的就不用关注
*/
throw new UnsupportedOperationException(); // 抛出一个不支持操作的异常
}
/**
* 将这个方法做成抽象的,因为所有的类都有这个方法去打印信息,所以将这个方法定义为抽象的,让
* 相应的子类去实现该方法
*/
public abstract void print();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
定义可以操作子节点和叶子节点
(即操作学院类和专业类)的学校类University
,该类通过组合上述的OrganizationComponent
来操作其他的子节点和叶子节点(即操作学院类和专业类)
,以此来消除该类与其他子节点和叶子节点之间依赖,将依赖关系转移到OrganizationComponent
这一个类上,以此来削减耦合,并继承OrganizationComponent类
来获取其共性,代码如下:
package edu.hebeu.combination;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
// 这个类相当于Composite,用来通过OrganizationComponent类管理College类
public class University extends OrganizationComponent {
/**
* 创建一个OrganizationComponent泛型的集合,用来通过OrganizationComponent类管理College类
*/
private Map<String, OrganizationComponent> colleges = new HashMap<>();
public University(String name, String desc) {
super(name, desc); // 调用父类的构造器
}
@Override
public void add(OrganizationComponent college) { // 重写add方法
colleges.put(college.getName(), college);
}
@Override
public void remove(OrganizationComponent college) { // 重写remove方法
colleges.remove(college.getName());
}
@Override
public OrganizationComponent find(String name) {
return colleges.get(name);
}
@Override
public void print() { // 实现print打印方法
System.out.println("************************" + getName() + "****************************"); // 输出大学(University类的name属性)的名字
System.out.println(" " + getDesc());
for (Entry<String, OrganizationComponent> college : colleges.entrySet()) { // 打印管理的类(即子类College)的信息
college.getValue().print(); // 调用子类的College 的print()方法
}
}
}
College子节点
的创建,注意要让该类继承OrganizationComponent
并实现需要的方法,以使University类
能通过OrganizationComponent类
操作到它,如下:
package edu.hebeu.combination;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class College extends OrganizationComponent {
/**
* 创建一个OrganizationComponent泛型的集合,用来通过OrganizationComponent类管
* 理Department类
*/
private Map<String, OrganizationComponent> departments = new HashMap<>();
public College(String name, String desc) {
super(name, desc); // 调用父类的构造器
}
@Override
public void add(OrganizationComponent department) { // 重写add方法
departments.put(department.getName(), department);
}
@Override
public void remove(OrganizationComponent department) { // 重写remove方法
departments.remove(department.getName());
}
@Override
public OrganizationComponent find(String name) {
return departments.get(name);
}
@Override
public void print() { // 重写print方法
System.out.print("\t\t\t##" + getName() + "##\n");
System.out.print("\t\t\t\t\t\t" + getDesc() + "\n");
for (Entry<String, OrganizationComponent> department : departments.entrySet()) { // 打印管理的类(即子类Department)的信息
department.getValue().print(); // 调用子类的Department 的print()方法
System.out.println("--------------------------------------------");
}
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++");
}
}
Department 子节点
的创建,注意要让该类继承OrganizationComponent
并实现需要的方法,以使College类
能通过OrganizationComponent类
操作到它,如下:
package edu.hebeu.combination;
public class Department extends OrganizationComponent {
public Department(String name, String desc) {
super(name, desc); // 调用父类的构造器
}
@Override
public void print() { // 重写print打印方法
System.out.print("\t\t\t" + getName() + "---" + getDesc() + "\n");
}
}
测试类Client
的编写
package edu.hebeu;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import edu.hebeu.combination.College;
import edu.hebeu.combination.Department;
import edu.hebeu.combination.OrganizationComponent;
import edu.hebeu.combination.University;
public class Client {
// 通过OrganizationComponent定义一个大学
private static OrganizationComponent UNIVERSITY;
private static Scanner SCANNER = new Scanner(System.in);
/**
* 该方法用来管理学院
*/
private static void managerCollege() {
String collegeName;
String collegeDesc;
boolean flag = true;
while(flag) {
System.out.println();System.out.println();
UNIVERSITY.print();
System.out.println("##############################################################################");
System.out.println("下辖学院(s)");
System.out.println("加入学院(a)");
System.out.println("删除学院(d)");
System.out.println("退出本模块(e)");
System.out.print("请选择您的服务:"); String keyword = SCANNER.next();
switch(keyword) {
case "s":
break;
case "a":
System.out.print("请输入要添加的学院名称:"); collegeName = SCANNER.next();
System.out.print("请输入要添加的学院描述:"); collegeDesc = SCANNER.next();
UNIVERSITY.add(new College(collegeName, collegeDesc));
break;
case "d":
System.out.print("请输入要删除的学院名称:"); collegeName = SCANNER.next();
UNIVERSITY.remove(new College(collegeName, null));
break;
case "e":
flag = false;
break;
default:
break;
}
}
}
/**
* 该方法用来管理专业
*/
private static void managerDepartment() {
String departName;
String departDesc;
OrganizationComponent college;
while(true) {
System.out.println();
System.out.print("请选择操作的学院:"); String collegeName = SCANNER.next();
college = UNIVERSITY.find(collegeName);
if(college != null) {
break;
}
System.err.println("未找到该学院!");
}
boolean flag = true;
while(flag) {
System.out.println();System.out.println();
college.print();
System.out.println("##############################################################################");
System.out.println("下辖专业(s)");
System.out.println("加入专业(a)");
System.out.println("删除专业(d)");
System.out.println("查询专业(f)");
System.out.println("退出本模块(e)");
System.out.print("请选择您的服务:");String keyword = SCANNER.next();
switch(keyword) {
case "s":
break;
case "a":
System.out.print("请输入要添加的专业名称:"); departName = SCANNER.next();
System.out.print("请输入要添加的专业描述:"); departDesc = SCANNER.next();
college.add(new Department(departName, departDesc));
break;
case "d":
System.out.print("请输入要删除的专业名称:"); departName = SCANNER.next();
OrganizationComponent department = college.find(departName);
if(department != null) {
college.remove(department);
}
break;
case "f":
System.out.print("请输入要查询的专业名称:"); departName = SCANNER.next();
OrganizationComponent department2 = college.find(departName);
if(department2 != null) {
department2.print();
}
break;
case "e":
flag = false;
break;
default:
break;
}
}
}
public static void main(String[] args) {
// "xxx大学", "本校开设诸多专业,上至天文,下到地理,学习风气浓厚,校风端正..."
System.out.print("请输入学校名称:"); String universityName = SCANNER.next();
System.out.print("请输入学校描述:"); String universityDesc = SCANNER.next();
UNIVERSITY = new University(universityName, universityDesc);
boolean flag = true;
while(flag) {
System.out.println();
UNIVERSITY.print();
System.out.println("学院管理(c)");
System.out.println("专业管理(d)");
System.out.println("退出程序(e)");
System.out.print("请选择您的服务:"); String keyword = SCANNER.next();
switch(keyword) {
case "c":
managerCollege();
break;
case "d":
managerDepartment();
break;
case "e":
flag = false;
break;
default:
break;
}
}
}
}
测试