设计模式系列之组合模式及其在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日

相关文章

  • java连接Oracle数据库的方法解析

    下面是Java连接Oracle数据库的方法解析的完整攻略。 一、准备工作 1.1 下载Oracle JDBC驱动 在Java连接Oracle数据库之前,需要先下载Oracle JDBC驱动程序,可以前往Oracle官网进行下载。 1.2 配置Oracle环境变量 将Oracle的安装目录加入环境变量中,以便Java程序能够正常连接Oracle数据库。 二、J…

    Java 2023年5月19日
    00
  • 记一次springboot中用undertow的坑

    记一次springboot中用undertow的坑 背景 在使用 Spring Boot 框架时,为了提高性能,可以选择使用 Undertow 作为 Web 容器。但是在实际应用中,使用 Undertow 可能会遇到一些坑。 问题 1. 配置路径匹配 使用 Undertow 时,如果需要配置路径匹配,需要在 application.properties 文件…

    Java 2023年5月19日
    00
  • 两种JAVA实现短网址服务算法

    下面是关于两种JAVA实现短网址服务算法的完整攻略。 一、算法介绍 在实现短网址服务时,通常需要将长URL转换为短字符串来实现,这时需要用到哈希算法。 解决方案一:MD5 MD5是一种广泛使用的哈希算法,它可以将任意长度的消息压缩为一个128位的哈希值。MD5哈希算法不可逆,因此可以很好地用来实现短网址服务。在此方案中,我们需要实现以下步骤: 获取长URL;…

    Java 2023年5月19日
    00
  • mybatis二级缓存的实现代码

    MyBatis是一款优秀的ORM框架,并支持一级和二级缓存,其中二级缓存存在于SqlSessionFactory的生命周期内,能够提高查询效率,本文将详细讲解MyBatis二级缓存的实现代码攻略。下面分以下几步进行讲解: 一、开启二级缓存 MyBatis默认是关闭二级缓存的,需要手动开启。在MyBatis的配置文件中添加一行配置: <!–开启二级缓存…

    Java 2023年6月1日
    00
  • logback的使用和logback.xml详解(小结)

    Logback的使用和logback.xml详解 Logback是一种高效和功能丰富的日志框架,它是log4j框架的升级版,而且使用非常简单。这里将介绍Logback的基本使用和配置文件logback.xml的详细解释。 Logback的基本使用 1. 添加Logback的依赖 首先,在项目的pom.xml文件中添加logback的依赖: <depen…

    Java 2023年5月20日
    00
  • java程序员必须知道的4个书写代码技巧

    Java程序员必须知道的4个书写代码技巧: 1. 编写清晰简洁的代码 编写清晰简洁的代码可以提高代码的可读性,让代码更易于维护和修改。以下是一些编写清晰简洁代码的技巧: 使用有意义的变量和函数命名,用英文单词或单词缩写命名变量和函数; 尽量减少重复代码,把重复的代码封装成函数或类; 避免使用魔法数值,使用常量代替魔法数值; 使用与语言标准一致的缩进格式和代码…

    Java 2023年5月23日
    00
  • 如何使用SpringSecurity保护程序安全

    当我们开发应用程序的时候,应该极力确保应用程序的安全性,因为数据安全至关重要。 SpringSecurity是一种开源安全框架,可以保护我们的应用程序,并确保具有良好的身份验证和授权,本文将详细讲解如何使用SpringSecurity保护程序安全。 SpringSecurity的基本概念 SpringSecurity是一种基于Servlet过滤器的安全框架,…

    Java 2023年5月20日
    00
  • fastjson 使用方法详细介绍

    Fastjson 使用方法详细介绍 Fastjson 是一款 Java 的 JSON 库,可以将 Java 对象与 JSON 互相转换。下面将详细介绍 Fastjson 的使用方法。 依赖引入 在使用 Fastjson 之前,需要先引入依赖。 Maven 依赖 在 pom.xml 文件中添加以下依赖: <dependency> <group…

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