设计模式详解(十一)——组合模式

时间:2024-02-22 11:21:37

组合模式简介

组合模式定义
组合模式(Composite Pattern)是一种结构型设计模式,又叫部分整体模式,它将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。

组合模式包含以下角色:

  1. Component(抽象构件):抽象构件可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类公有行为的声明和实现,包括添加(add)、删除(remove)、获取子组件(getChildren)以及实现业务逻辑的方法(operation)。它是一个定义了公共的操作方法,可以用于管理和访问component子部件的抽象对象。
  2. Leaf(叶子构件):叶子构件在组合结构中表示叶子节点对象,叶子节点没有子节点,实现了抽象组件接口或抽象类,但没有子组件。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。叶子节点是树形结构中的最底层对象,通常包含具体的业务逻辑实现。
  3. Composite(树枝节点构件):枝节点构件在组合结构中表示容器节点对象,枝节点节点包含子节点,其子节点可以是叶子节点,也可以是其它枝节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义行为,包括那些访问及管理子构件对方法,在其业务方法中可以递归调用其子节点对业务方法。并允许客户端以统一的方式处理其子组件。
  4. Client(客户类):使用组合模式来创建和操作复合对象结构。客户端代码不需要关心对象是叶子节点还是树枝节点,因为所有对象都通过抽象组件接口或抽象类来访问。

组合模式优缺点:
优点:

  1. 客户端代码可以一致地处理单个对象和组合对象,无需关心它们之间的区别。简化了客户端代码;
  2. 组合模式可以优化处理递归或分级数据结构,因为它将对象组织成树形结构,使得对对象的处理更加直观和高效。
  3. 容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

缺点:

  1. 设计复杂性:组合模式可能使设计变得更加复杂,特别是当类之间的层次关系变得复杂时。客户端需要花更多时间来理解这些关系,从而正确地使用组合模式。
  2. 限制容器中的构件:在组合模式中,当需要添加新的容器构件或叶子构件时,可能很难对容器中的构件类型进行限制。这可能导致设计上的混乱和不必要的复杂性。
  3. 继承方法的限制:组合模式通常使用继承来实现,但这可能会限制新的构件只能通过继承来添加新功能。这可能限制了系统的灵活性和可扩展性。
  4. 依赖倒置原则的违反:在某些实现中,组合模式的叶子和树枝的声明可能是实现类而不是接口,这违反了依赖倒置原则。这可能导致代码之间的耦合度过高,不利于维护和扩展。

使用场景

  1. 表示对象的部分和整体层次结构:当需要表示一个对象的部分以及整体层次结构时,组合模式是一个很好的选择。例如,在文件系统中,根目录下有若干文件和目录,在二级目录下还有目录和文件,这种情况下适合使用组合模式。
  2. 忽略组合对象与单个对象的差异:如果客户端需要忽略组合对象与单个对象的差异,以一致的方式处理它们,那么可以使用组合模式。这样,客户端代码可以简化,因为无需关心处理的是单个对象还是组合对象。
  3. 处理递归或分级数据结构:组合模式可以优化处理递归或分级数据结构。通过将对象组织成树形结构,组合模式使得对这些对象的处理更加直观和高效。

通俗来讲就是:
比如在现实生活中,存在很多“部分-整体”的关系。汽车与轮胎、发动机的关系。警察局与科室、警察的关系。学校与学院、学生、老师的关系。
组合模式是一种非常普遍和常用的模式,接口服务互相组合,提供更丰富的接口,实现复杂的业务逻辑。一般情况会选择使用组合代替继承,组合更灵活、更方便。

以下举一个组合模式的例子:
假设学校中有校长管理老师,老师学生,通过以下实例演示学校中人员的层次结构:

创建抽象类,组件Persion 【Component(组件)】

import java.util.List;

/**
 * Component(组件)
 *
 * 为组合中的对象声明接口
 * 声明一个接口用于访问和管理Component的子组件
 */

public abstract class Persion {
    private String name;
    protected List<Persion> persionList;

    public abstract void add(Persion persion);
    public abstract void delete(Persion persion);

    public void input() {
        System.out.println(String.format("name is %s", name));
    };

    public String getName() {
        return name;
    }

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

    public List<Persion> getPersionList() {
        return persionList;
    }
}

创建具体类Principal【Composite(树枝节点构件)】

import java.util.ArrayList;

/**
 * Composite(树枝节点构件)
 *
 * 实现了在抽象构件中定义行为,包括那些访问及管理子构件对方法
 * 存储子部件
 *
 */
public class Principal extends Persion {
    public Principal(String name) {
        setName(name);
        persionList = new ArrayList<Persion>();
    }

    @Override
    public void add(Persion persion) {
        persionList.add(persion);
    }

    @Override
    public void delete(Persion persion) {
        persionList.remove(persion);
    }
}

创建具体类Teacher【Composite(树枝节点构件)】

import java.util.ArrayList;

/**
 * Composite(树枝节点构件)
 *
 * 实现了在抽象构件中定义行为,包括那些访问及管理子构件对方法
 * 存储子部件
 *
 */
public class Teacher extends Persion {
    public Teacher(String name) {
        setName(name);
        persionList = new ArrayList<Persion>();
    }

    @Override
    public void add(Persion persion) {
        persionList.add(persion);
    }

    @Override
    public void delete(Persion persion) {
        persionList.remove(persion);
    }
}

创建具体类Student【Leaf(叶子构件)】

/**
 * Leaf(叶子构件)
 *
 * 叶子构件在组合结构中表示叶子节点对象,叶子节点没有子节点
 * 对于那些访问及管理子构件的方法,可以通过异常等方式进行处理
 */
public class Student extends Persion{
    public Student(String name) {
        setName(name);
        persionList = null;
    }

    @Override
    public void add(Persion persion) {
    }

    @Override
    public void delete(Persion persion) {
    }
}

创建测试类【Client(客户类)】

/**
 * Client(客户类)
 * 使用组合模式来创建和操作复合对象结构
 *
*/
public class Client {
    public static void main(String[] args) {
        Persion principal = new Principal("校长");
        Persion teacher1 = new Teacher("教师1");
        Persion teacher2 = new Teacher("教师2");
        Persion student1 = new Student("学生1");
        Persion student2 = new Student("学生2");
        Persion student3 = new Student("学生3");
        Persion student4 = new Student("学生4");

        principal.add(teacher1);
        principal.add(teacher2);
        teacher1.add(student1);
        teacher1.add(student2);
        teacher2.add(student3);
        teacher2.add(student4);

        info("无需", principal);
    }

    public static void info(String pre ,Persion persion){
        System.out.println(String.format("%s管理:%s", pre, persion.getName()));

        if(null == persion.getPersionList()){
            return;
        }
        for(Persion persion1 : persion.getPersionList()){
            info(persion.getName(), persion1);
        }

    }

}

输出结果如下所示:

无需管理:校长
校长管理:教师1
教师1管理:学生1
教师1管理:学生2
校长管理:教师2
教师2管理:学生3
教师2管理:学生4

从结果可以看成功根据需求,获取到了校长管理老师,老师管理学生的层级结构。

总而言之:
组合模式,它是一种非常强大的结构型设计模式,用于创建复杂的对象结构,允许用户以统一的方式处理单个对象和复合对象。通过组合模式,你可以构建出具有层次结构的对象模型,这些对象可以像单个对象一样被简单、一致地使用。
组合模式提供一个结构,可同时包容个别对象和组合对象;允许客户对个别对象以及组合对象一视同仁;组合结构内的任意对象称为组件;组件可以是组合,也可以是叶节点;在使用组合模式时,要多加考虑方式,有的时候可以与迭代器模式配合使用。


以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git