avatar

java组合模式

基本介绍

  • 组合模式(Composite Pattern):组合多个对象形成树形结构以表示”整体-部分”的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
  • 组合模式又可以称为“整体-部分”(Part-Whole)模式,属于对象的结构模式,它将对象组织到树结构中,可以用来描述整体与部分的关系。
  • 组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它可以提供一致的方式,而不用考虑它是叶子还是节点

    结构图

    组合模式包含如下角色:
  • Component: 抽象构件,组合对象声明接口,在适当情况下实现所有类共有的接口默认行为,用于访问和管理Component子部件,可以是抽象类也可以是接口
  • Leaf: 叶子构件,组合中的叶子节点,没有子节点
  • Composite: 容器构件,非叶子节点,用于存储子部件,在Component接口中实现子部件的相关操作
  • Client: 客户类

代码实现

Component为组合中的对象声明接口,在适当情况下,实现所以类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件

public abstract class Component
{
protected String name;

public Component(String name) {
this.name = name;
}
//通常提供add与Remove方法来提供增加或者移除树叶的功能
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}

定义一个叶子节点

public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
//由于叶子节点不在增加分支和树叶,所以Add和Remove方法实现它没有意义,但是这样做可以消除页节点和枝节点对象在抽象层次的区别,他们具备完全一致的接口
@Override
public void Add(Component c) {
System.out.println("不能添加叶子节点");
}

@Override
public void Remove(Component c) {
System.out.println("不能再移除叶子节点");
}
//叶子节点的具体方法
@Override
public void Display(int depth) {
System.out.println(depth+"-"+name);
}
}

Composite定义有枝节点行为,用于存储子部件,在Component接口中实现与子部件有关的操作。比如增加add和删除Remove

public class Composite extends Component {
//用来存储自己的孩子节点
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}

@Override
public void Add(Component c) {
children.add(c);
}

@Override
public void Remove(Component c) {
children.remove(c);
}

@Override
public void Display(int depth) {
System.out.println(depth+"-"+name);
for (Component c: children) {
c.Display(depth+2);
}
}
}

写一个客户端去调用一下

public class Client {
public static void main(String[] args)
{
Composite root = new Composite("root");//定义一个根节点
root.Add(new Leaf("leaf A"));
root.Add(new Leaf("leaf B"));

Composite comp = new Composite("Composite X");
comp.Add(new Leaf("leaf xA"));
comp.Add(new Leaf("leaf xB"));

root.Add(comp);

Composite comp2 = new Composite("COmposite XY");
comp2.Add(new Leaf("Leaf XYA"));
comp2.Add(new Leaf("Leaf XYB"));

comp.Add(comp2);

root.Display(1);
}
}

这样的输出就带有树的样子,会有层次结构并且每个部门都有公共的方法,想要自己是的实现也可以单独另外写,这样做非常方便把层级结构表现出来。

透明方式和安全方式

透明方式

在Component中声明所有用来管理子类的方法,其中包括add,romove等,这样实现Component接口的所有子类都具备了Add和Remove,这样做的好处是叶子节点和枝节点对外界没有区别,它们具备完全一致的行为接口。但是问题也很明显,因为Leaf类本身不具备Add(),Remove()方法的功能,所以实现它是没有意义的

安全模式

在Component接口中不去申明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite中声明所有管理子类对象的方法,这样做就不会出现刚才提到的问题不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来不方便。

例子

来看一下我们的大学也是一种树状结构,大学底下有学院,学院底下有院系专业。这也是一种层级结构,也可以使用组合模式进行表达,我们看一下类图

类图

其中OrganizationComponent是所有类型的基类,其中有两个层级,大层级是University,小层级是College,最小的叶子节点是Department。
University包含College,College中包含Department。

代码实现

首先我们需要写一个OrganizationComponent基类

public abstract class OrganizationComponent
{
private String name;
private String des;
protected void Add(OrganizationComponent organizationComponent)
{
//有一个默认实现
throw new UnsupportedOperationException();
}
protected void Remove(OrganizationComponent organizationComponent)
{
//有一个默认实现
throw new UnsupportedOperationException();
}
//写一个构造器
public OrganizationComponent(String name, String des) {
this.name = name;
this.des = des;
}

public void setName(String name) {
this.name = name;
}

public void setDes(String des) {
this.des = des;
}

public String getName() {
return name;
}

public String getDes() {
return des;
}
//获取一个打印方法,这个方法有自己的子类去实现
protected abstract void prient();
}

我们编写University类

public class University extends OrganizationComponent {

List<OrganizationComponent> organizationComponents = new ArrayList<>();//聚合自己得管理类,自己得子集
public University(String name, String des) {
super(name, des);
}
@Override
protected void Add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
@Override
protected void Remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
protected void prient() {
System.out.println(getName()+"----"+getDes());
for (OrganizationComponent o: organizationComponents) {
o.prient();
}
}
}
//College代码是一样得。
public class College extends OrganizationComponent {

List<OrganizationComponent> organizationComponents = new ArrayList<>();//聚合自己得管理类,自己得子集

public College(String name, String des) {
super(name, des);
}

@Override
protected void Add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}

@Override
protected void Remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}

@Override
protected void prient() {
System.out.println(getName() + "----" + getDes());
for (OrganizationComponent o : organizationComponents) {
o.prient();
}
}
}

然我们编写Department代码,也是组合模式中得最小单元

public class Department extends OrganizationComponent {
@Override
protected void prient() {
System.out.println(getName()+"----------"+getDes());
}

public Department(String name, String des) {
super(name, des);
}
}

然后我们编写一个客户端进行一下调用

public class Client {
public static void main(String[] args) {
University university = new University("山东科技大学","我就读得大学");

College chcollege = new College("测绘科学与工程学院","我第一专业");
College Mathcollege = new College("数学学院","我得第二专业");

Department department = new Department("GIS","非常好玩");
Department department1 = new Department("RS","也非常好玩");

Department department2 = new Department("信息与计算科学","很难");
Department department3 = new Department("统计学","也很难");

university.Add(chcollege);
university.Add(Mathcollege);//两个学院属于山科

chcollege.Add(department);
chcollege.Add(department1); //GIS RS属于测绘学院

Mathcollege.Add(department2);
Mathcollege.Add(department3);

university.prient();
}
}
//输出为
/*
*山东科技大学----我就读得大学
测绘科学与工程学院----我第一专业
GIS----------非常好玩
RS----------也非常好玩
数学学院----我得第二专业
信息与计算科学----------很难
统计学----------也很难
*/

总结

  • 简化客户端操作,客户端只需要面对一致得对象而不用考虑整体部分或者叶子节点得问题
  • 具有较强得扩展性。当我们需要更改组合对象得时候,只需要调整内部得层次关系,客户端不需要做出任何改动
  • 方便创建出复杂得层次结构。客户端不用理会组合里面得组成细节,容易添加节点或者叶子从而创建出复杂得树形结构
  • 需要遍历组织机构,或者处理得对象具有树形结构时,非常适合使用组合模式
  • 要求较高得抽象性,如果节点和叶子有很多差异性得话,比如很多方法和属性都不一样,那么不适合使用组合模式
文章作者: zenshin
文章链接: https://zlh.giserhub.com/2020/04/15/DesignPattern/composite/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论