设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

请看下面的完整攻略:

设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解

什么是组合模式

组合模式(Composite Pattern),也叫部分-整体模式,是一种结构型设计模式。通过将对象组合成树形结构,以表示“整体-部分”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,即将对象的组合与单个对象的使用同等对待。

组合模式由以下角色构成:

  • Component(抽象构件):是组合中对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
  • Leaf(叶子构件):在组合中表示叶子节点对象,叶子节点没有子节点。
  • Composite(容器构件):表示容器组件,容器组件包含一些子节点,用来定义有枝节点的行为,实现在Component接口中的有关操作,如增加(add)和删除(remove)子节点等。

组合模式的优缺点

组合模式的优点在于:

  • 定义了包含基本对象和组合对象的类层次结构。
  • 简化了客户端代码,使得客户端可以像处理单个对象一样来处理组合对象。
  • 更容易增加新的组合对象和叶子对象。

组合模式的缺点在于:

  • 可能会增加系统的复杂性。
  • 实现组合模式需要遵循一定的规则和约束,否则可能会破坏整个模式结构。

组合模式在JDK中的运用

在JDK中,使用组合模式的典型例子就是针对集合类框架中的容器和叶子节点的设计。

以ArrayList为例,其源码如下:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    // Object array buffer for ArrayList
    private transient Object[] elementData;

    // ...

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    // ...

}

在ArrayList中,添加元素的方法add就是一个容器对象,而ArrayList中存储的元素则是叶子节点对象。ArrayList内部维护了一个Object数组作为容器,当添加元素时,向该数组中增加元素作为叶子节点。

组合模式在MyBatis中的运用

在MyBatis中,组合模式被应用到SQL语句的构建和执行过程中。

以SqlNode为例,其源码如下:

public interface SqlNode {

  boolean apply(DynamicContext context);

  // ...

}


public class MixedSqlNode implements SqlNode {

  private List<SqlNode> contents;

  // ...

  @Override
  public boolean apply(DynamicContext context) {
      Iterator<SqlNode> iter = contents.iterator();
      while (iter.hasNext()) {
        SqlNode sqlNode = iter.next();
        sqlNode.apply(context);
      }
      return true;
  }

}

在MyBatis中,SQL语句的执行由对SqlNode的应用开展,存储SQL语句的主干是MixedSqlNode作为容器,SqlNode则表示叶子节点。在SqlNode接口中定义了apply方法,该方法对节点进行处理,而在MixedSqlNode中则通过迭代处理了其内部维护的SqlNode节点。

示例一:文件系统的案例

假设我们要实现一个文件系统,其中各个目录和文件节点是可以随意嵌套的。其中,文件节点可以进行读写操作,而目录节点则可以管理它包含的子节点,并对其进行各种基本操作。

下面是用Java代码实现的文件系统:

// Component: 抽象文件系统节点
public abstract class FileSystemNode {

    public abstract void add(FileSystemNode node);

    public abstract void remove(FileSystemNode node);

    public abstract String getName();

    public abstract int getSize();

    public String getInfo() {
        return getName() + " (" + getSize() + ")";
    }

}

// Leaf: 文件节点
public class FileNode extends FileSystemNode {

    private String name;

    private int size;

    public FileNode(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public void add(FileSystemNode node) {
        throw new UnsupportedOperationException();
    }

    public void remove(FileSystemNode node) {
        throw new UnsupportedOperationException();
    }

    public String getName() {
        return name;
    }

    public int getSize() {
        return size;
    }

}

// Composite: 目录节点
public class DirectoryNode extends FileSystemNode {

    private String name;

    private List<FileSystemNode> nodes = new ArrayList<>();

    public DirectoryNode(String name) {
        this.name = name;
    }

    public void add(FileSystemNode node) {
        nodes.add(node);
    }

    public void remove(FileSystemNode node) {
        nodes.remove(node);
    }

    public String getName() {
        return name;
    }

    public int getSize() {
        int size = 0;
        for (FileSystemNode node : nodes) {
            size += node.getSize();
        }
        return size;
    }

    public List<FileSystemNode> getNodes() {
        return nodes;
    }

}

// Client: 测试类
public class FileSystemClient {

    public static void main(String[] args) {
        // 创建文件节点
        FileSystemNode file1 = new FileNode("file1.txt", 10);
        FileSystemNode file2 = new FileNode("file2.txt", 20);
        FileSystemNode file3 = new FileNode("file3.txt", 30);

        // 创建目录节点
        DirectoryNode dir1 = new DirectoryNode("dir1");
        DirectoryNode dir2 = new DirectoryNode("dir2");

        // 添加文件节点到目录节点
        dir1.add(file1);
        dir1.add(file2);
        dir2.add(file3);

        // 添加目录节点到根节点目录
        DirectoryNode root = new DirectoryNode("root");
        root.add(dir1);
        root.add(dir2);

        // 输出各个节点信息
        printNodeInfo(root);
    }

    private static void printNodeInfo(FileSystemNode node) {
        System.out.println(node.getInfo());
        if (node instanceof DirectoryNode) {
            for (FileSystemNode sub : ((DirectoryNode) node).getNodes()) {
                printNodeInfo(sub);
            }
        }
    }

}

在上述代码中,FileSystemNode作为抽象构件类,FileNode和DirectoryNode则作为具体构件类实现。在DirectoryNode中,通过List维护了一个子节点列表,并在add和remove方法中对其进行更新,同时在getSize方法中计算了目录节点的大小。在FileSystemClient中对各个节点进行初始化,并通过递归地方式遍历输出节点信息。

示例二:公司组织结构的案例

假设我们要实现一个公司组织结构,其中员工和部门可以任意嵌套,员工节点可以获取和修改自己的信息,部门节点则可以管理它包含的部门和员工,并对其进行各种基本操作。

下面是用Java代码实现的公司组织结构:

// Component: 抽象组织节点
public abstract class OrganizationNode {

    protected String name;

    public OrganizationNode(String name) {
        this.name = name;
    }

    public abstract void add(OrganizationNode node);

    public abstract void remove(OrganizationNode node);

    public abstract List<OrganizationNode> getChildren();

    public abstract String getInfo();

    public String getName() {
        return name;
    }

}

// Leaf: 员工节点
public class EmployeeNode extends OrganizationNode {

    private String title;

    public EmployeeNode(String name, String title) {
        super(name);
        this.title = title;
    }

    public void add(OrganizationNode node) {
        throw new UnsupportedOperationException();
    }

    public void remove(OrganizationNode node) {
        throw new UnsupportedOperationException();
    }

    public List<OrganizationNode> getChildren() {
        return Collections.emptyList();
    }

    public String getInfo() {
        return "Employee " + name + " (" + title + ")";
    }

    // 获取和修改员工信息的方法
    // ...

}

// Composite: 部门节点
public class DepartmentNode extends OrganizationNode {

    private List<OrganizationNode> children = new ArrayList<>();

    public DepartmentNode(String name) {
        super(name);
    }

    public void add(OrganizationNode node) {
        children.add(node);
    }

    public void remove(OrganizationNode node) {
        children.remove(node);
    }

    public List<OrganizationNode> getChildren() {
        return children;
    }

    public String getInfo() {
        return "Department " + name;
    }

}

// Client: 测试类
public class OrganizationClient {

    public static void main(String[] args) {
        // 创建员工节点
        OrganizationNode employee1 = new EmployeeNode("001", "CEO");
        OrganizationNode employee2 = new EmployeeNode("002", "CFO");
        OrganizationNode employee3 = new EmployeeNode("003", "CTO");

        // 创建部门节点
        OrganizationNode development = new DepartmentNode("Development");
        OrganizationNode finance = new DepartmentNode("Finance");
        OrganizationNode marketing = new DepartmentNode("Marketing");

        // 添加员工节点到部门节点
        development.add(employee3);

        finance.add(employee2);

        marketing.add(employee1);

        // 添加部门节点到总节点
        OrganizationNode root = new DepartmentNode("Company");
        root.add(development);
        root.add(finance);
        root.add(marketing);

        // 打印组织结构信息
        printNodeInfo(root, "");
    }

    private static void printNodeInfo(OrganizationNode node, String prefix) {
        System.out.println(prefix + node.getInfo());
        for (OrganizationNode sub : node.getChildren()) {
            printNodeInfo(sub, prefix + "\t");
        }
    }

}

在上述代码中,OrganizationNode作为抽象构件类,EmployeeNode和DepartmentNode则作为具体构件类实现。在DepartmentNode中,通过List维护了一个子节点列表,并在add和remove方法中对其进行更新,同时在getChildren方法中返回节点的所有子节点。在OrganizationClient中对各个节点进行初始化,并通过递归地方式遍历输出节点信息。同时,EmployeeNode中实现了获取和修改员工信息的方法。

以上是组合模式及其在JDK和MyBatis源码中的运用详解,代码中的示例都展示了组合模式的核心特点:将节点组织成树形结构,使其更容易被管理和操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:设计模式系列之组合模式及其在JDK和MyBatis源码中的运用详解 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Centos6.x服务器配置jdk+tomcat+mysql环境(jsp+mysql)

    以下是CentOS 6.x服务器配置JDK+Tomcat+MySQL环境的攻略: 1. 安装JDK 在CentOS 6.x系统上安装JDK可以使用如下命令: yum install java-1.8.0-openjdk-devel 安装完毕之后,可以通过下面的命令查看是否已经安装成功: java -version 2. 安装Tomcat CentOS 6.x…

    Java 2023年5月19日
    00
  • 使用JPA进行CriteriaQuery进行查询的注意事项

    使用JPA进行CriteriaQuery进行查询时,需要注意以下几个方面: 1. 配置persistence.xml 首先,需要在persistence.xml文件中配置JPA的provider和数据库连接信息。在provider中需要指定使用Hibernate等JPA实现,以及指定JPA的版本。例如: <persistence-unit name=&…

    Java 2023年5月20日
    00
  • Flash 实用代码总汇第1/2页

    我们来详细讲解一下“Flash 实用代码总汇第1/2页”的完整攻略。 1. 概述 本篇攻略主要介绍了 Flash 实用代码总汇第1/2页 的使用方法,其中包含了有关 Flash 常用代码的分类、查找和使用等方面的内容。该代码总汇包含了许多 Flash 动画制作过程中可能用到的代码,对于 Flash 初学者或是想要提高 Flash 制作技能的人来说都是非常有用…

    Java 2023年6月15日
    00
  • Spring boot整合mybatis实现过程图解

    首先我们来讲解一下Spring Boot整合Mybatis的基本过程和步骤: 1. 引入相关依赖 首先我们需要在我们的pom.xml文件中引入以下依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boo…

    Java 2023年5月19日
    00
  • java动态规划算法——硬币找零问题实例分析

    Java 动态规划算法——硬币找零问题实例分析 简介 硬币找零问题是一类非常经典的问题,主要是如何计算出需要多少硬币才能凑够给定的金额。动态规划是解决硬币找零问题的一种常用算法。本文将介绍动态规划算法的工作原理及其在硬币找零问题中的应用。 动态规划算法 动态规划算法(Dynamic Programming)是一种解决问题的思想,它将问题拆分成若干个子问题,并…

    Java 2023年5月26日
    00
  • IntelliJ IDEA下Maven创建Scala项目的方法步骤

    下面是详细的攻略步骤: 一、前置条件 在开始之前,需要你已经将IntelliJ IDEA和Maven安装并配置好。如果还没有安装和配置,请先安装和配置。 二、创建Maven项目 打开IntelliJ IDEA,选择“File”-“New”-“Project”,在选择窗口中选择Maven,并点击“Next”; 在“New Project”对话框中,填写项目相关…

    Java 2023年5月20日
    00
  • 什么是递归?用Java写一个简单的递归程序

    什么是递归? 递归是一种解决问题的方法。它将问题分解为子问题,并通过递归调用函数来解决这些子问题。递归函数是一个函数,它调用它本身,直到达到某个终止条件。 Java中的递归 在Java中,递归是通过调用函数本身来实现的。下面是一个递归函数的一般形式: public returnType functionName(parameters) { if (baseC…

    Java 2023年5月24日
    00
  • SpringBoot Pom文件依赖及Starter启动器详细介绍

    下面是关于“SpringBoot Pom文件依赖及Starter启动器详细介绍”的详细攻略。 SpringBoot Pom文件依赖 什么是Pom文件 Pom是Maven项目管理器的核心配置文件,它作为Maven构建工具的主要配置文件,被用来定义一个项目的依赖、构建、测试等配置信息。 SpringBoot Pom文件的作用 在进行SpringBoot项目开发的…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部