MyBatis源码中的设计模式2

news/2024/8/26 12:31:12 标签: mybatis, 设计模式

组合模式的应用

组合模式介绍

组合模式(Composite Pattern) 的定义是:将对象组合成树形结构以表示整体和部分的层次结构。组合模式可以让用户统一对待单个对象和对象的组合。

比如:Windows操作系统中的目录结构,通过tree命令实现树形结构展示。

在这里插入图片描述

在上图中包含了文件夹和文件两类不同元素,其中在文件夹中可以包含文件,还可以继续包含子文件夹。子文件夹中可以放入文件,也可以放入子文件夹。 文件夹形成了一种容器结构(树形结构),递归结构。

在这里插入图片描述

尽管文件夹和文件是不同类型的对象,它们有一个共性,就是都可以被放入文件夹中。文件和文件夹可以被当做是同一种对象看待。

组合模式其实就是将一组对象(文件夹和文件)组织成树形结构,以表示一种“部分-整体”的层次结构(目录与子目录的嵌套结构)。组合模式让客户端可以统一处理单个对象(文件)和组合对象(文件夹)的逻辑(递归遍历)。

组合模式更像是一种数据结构和算法的抽象,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。

在这里插入图片描述

组合模式主要包含三种角色:

  • 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。

    • 包含所有子类共有行为的声明和实现。在抽象根节点中定义了访问及管理子构件的方法,如增加子节点、删除子节点、获取子节点等。
  • 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。

    • 树枝节点可以包含树枝节点,也可以包含叶子节点。它其中有一个集合可以用于存储子节点,包含在抽象根节点中定义的行为。业务方法中可以递归调用其子节点的业务方法。
  • 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。

    • 叶子节点没有子节点,包含在抽象根节点中定义的行为。

组合模式示例

程序功能:列出某一目录下所有的文件和文件夹。

类图如下:

在这里插入图片描述

我们按照下图的表示,进行文件和文件夹的构建。

在这里插入图片描述

Entry类: 抽象类,用来定义File类和Directory类的共性内容

/**
 * Entry抽象类,表示目录条目(文件+文件夹)的抽象类
 */
public abstract class Entry {

    public abstract String getName(); // 获取文件名

    public abstract int getSize(); // 获取文件大小

    // 添加文件夹或文件
    public abstract Entry add(Entry entry);

    // 显示指定目录下的所有信息
    public abstract void printList(String prefix);

    @Override
    public String toString() {
        return getName() + "(" + getSize() + ")";
    }
}

File类: 叶子节点,表示文件

/**
 * File类 表示文件
 */
public class File extends Entry {

    private String name; // 文件名
    private int size; // 文件大小

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

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public Entry add(Entry entry) {
        return null; // 叶子节点不能添加子节点
    }

    @Override
    public void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }
}

Directory类: 树枝节点,表示文件夹

/**
 * Directory表示文件夹
 */
public class Directory extends Entry {

    private String name; // 文件夹名
    private ArrayList<Entry> directory = new ArrayList<>(); // 文件夹与文件的集合

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

    @Override
    public String getName() {
        return this.name;
    }

    /**
     * 获取文件大小
     * 1.如果entry对象是File类型,则调用getSize方法获取文件大小
     * 2.如果entry对象是Directory类型,会继续调用子文件夹的getSize方法,形成递归调用.
     */
    @Override
    public int getSize() {
        int size = 0;
        for (Entry entry : directory) {
            size += entry.getSize();
        }
        return size;
    }

    @Override
    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }

    @Override
    public void printList(String prefix) {
        System.out.println(prefix + "/" + this);
        for (Entry entry : directory) {
            entry.printList(prefix + "/" + name);
        }
    }
}

测试代码

public class Client {

    public static void main(String[] args) {

        // 根节点
        Directory rootDir = new Directory("root");

        // 树枝节点
        Directory binDir = new Directory("bin");
        // 向bin目录中添加叶子节点
        binDir.add(new File("vi", 10000));
        binDir.add(new File("test", 20000));

        Directory tmpDir = new Directory("tmp");

        Directory usrDir = new Directory("usr");
        Directory mysqlDir = new Directory("mysql");
        mysqlDir.add(new File("my.cnf", 30));
        mysqlDir.add(new File("test.db", 25000));
        usrDir.add(mysqlDir);

        rootDir.add(binDir);
        rootDir.add(tmpDir);
        rootDir.add(mysqlDir);

        rootDir.printList("");
    }
}

组合模式优点

  1. 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  2. 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
  3. 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

组合模式缺点

  • 使用组合模式的前提在于,你的业务场景必须能够表示成树形结构。所以,组合模式的应用场景也比较局限,它并不是一种很常用的设计模式

组合模式使用场景分析

  • 处理一个树形结构,比如,公司人员组织架构、订单信息等;
  • 跨越多个层次结构聚合数据,比如,统计文件夹下文件总数;
  • 统一处理一个结构中的多个对象,比如,遍历文件夹下所有 XML 类型文件内容。

MyBatis中的应用

MyBatis支持动态SQL的强大功能,比如下面的这个SQL:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
    UPDATE users
    <trim prefix="SET" prefixOverrides=",">
        <if test="name != null and name != ''">
            name = #{name}
        </if>
        <if test="age != null and age != ''">
            , age = #{age}
        </if>
        <if test="birthday != null and birthday != ''">
            , birthday = #{birthday}
        </if>
    </trim>
    where id = ${id}
</update>

在这里面使用到了trim、if等动态标签,我们可以根据实际的业务需求,动态地拼装SQL语句。这些动态SQL标签在MyBatis中被解析后会被转换为不同的SQL节点树结构,通过组合模式将这些SQL节点组织在一起,最后生成完整的SQL语句。

MyBatis中用组合模式的例子有MixedSqlNodeTrimSqlNodeChooseSqlNodeIfSqlNodeWhereSqlNode等。

我们通过查看MixedSqlNode类的源码,可以看到组合模式的应用。

public class MixedSqlNode implements SqlNode {
    private final List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    @Override
    public boolean apply(DynamicContext context) {
        for (SqlNode sqlNode : contents) {
            sqlNode.apply(context);
        }
        return true;
    }
}

MixedSqlNode类中,包含了一个List<SqlNode>集合contents,它可以包含SqlNode类型的对象,如IfSqlNodeTrimSqlNodeWhereSqlNode等。这些节点可以组成一个复杂的树形结构,通过递归调用它们的apply方法,可以逐步生成完整的SQL语句。

例如,IfSqlNode类的源码:

public class IfSqlNode implements SqlNode {
    private final ExpressionEvaluator evaluator;
    private final String test;
    private final SqlNode contents;

    public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
    }

    @Override
    public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }
}

IfSqlNode中,它包含一个SqlNode类型的contents,表示如果条件满足时需要执行的SQL节点。通过调用contents.apply(context),可以将子节点的内容拼接到当前的SQL上下文中。

总结

组合模式在处理复杂结构时非常有用,尤其是树形结构和递归处理的场景。它可以通过统一接口处理单个对象和组合对象,简化了代码的实现和维护。在MyBatis中,组合模式被广泛应用于动态SQL的生成过程中,通过不同类型的SQL节点组织成树形结构,递归地生成最终的SQL语句。

通过以上内容,我们了解了组合模式的定义、结构、优缺点以及在实际中的应用,特别是在MyBatis中的具体实现。掌握组合模式可以帮助我们更好地设计和实现复杂结构的代码,提高代码的可维护性和扩展性。


http://www.niftyadmin.cn/n/5558089.html

相关文章

关于window配置gitlab和gitee平台共存

今天使用gitlab拉取代码突然提示 gitgitlab.xxx.com: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password). 以为是ssh公钥没有配置好&#xff0c;遂又进行了一番配置&#xff0c;实际上并不是这个问题造成的&#xff0c;但还是想记录一下步骤&#xff0c;以…

C++链接FTP服务器并下载数据(在qt中编写)

.pro文件 #------------------------------------------------- # # Project created by QtCreator 2024-07-16T13:19:03 # #-------------------------------------------------QT core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET untitled TE…

机器学习评价指标之决策曲线

决策曲线是一种用于评估和比较不同分类模型性能的工具&#xff0c;它可以帮助研究人员和数据分析者理解模型在不同阈值设置下的收益和风险。以下是一些关于分类模型决策曲线的详细信息&#xff1a; 决策曲线的构成&#xff1a; 阈值&#xff08;Threshold&#xff09;&#xf…

YOLOv8目标跟踪deepsort

原文:YOLOv8目标跟踪deepsort - 知乎 (zhihu.com) 一、YOLOV8 tracking 参考文章: Ctrl CV:YoloV5 + DeepSort1 赞同 0 评论文章 二、行人重识别(ReID) ——Market-1501 数据集 2.1、数据集简介 Market-1501 数据集在清华大学校园中采集,夏天拍摄,在 2015 年构建并…

Python环境安装参考(python解释器+Pycharm软件)

Python代码经过Python解释器的处理可以生成能让计算机理解的二进制代码 python解释器安装&#xff1a; Python的下载安装&#xff08;手把手教学&#xff09;_如何下载python-CSDN博客文章浏览阅读3.7w次&#xff0c;点赞53次&#xff0c;收藏274次。程序员养成日记第一天&a…

在线图源或者离线资源的国家队-天地图简介及Qgis加载相关资源实战

目录 前言 一、如何注册天地图 1、进入天地图官网 2、登录天地图 二、天地图的功能简介 1、地图服务 2、开发资源 3、服务中心 三、如何使用Qgis加载天地图的底图 1、tianditu 工具插件 2、dianditu插件的使用 2、xyz瓦片加载 总结 前言 相信作为地理信息方面的从…

ubuntu 18 cuda 11.8 安装 vllm

根据官方文档 # Install vLLM with CUDA 11.8. export VLLM_VERSION0.4.0 export PYTHON_VERSION310 pip install https://github.com/vllm-project/vllm/releases/download/v${VLLM_VERSION}/vllm-${VLLM_VERSION}cu118-cp${PYTHON_VERSION}-cp${PYTHON_VERSION}-manylinux1_…

[Linux+git+Gitee+Jenkins]持续集成实验安装配置详细

首先理解持续集成原理&#xff0c;看懂并理解图 1。 图 1 持续集成原理结构 图 1 中&#xff0c;版本控制服务器指远程代码仓库&#xff0c;本实验使用 GitEE 作为远程代码仓库&#xff1b;Jenkins 自动化部署服务器为虚拟机&#xff0c;操作系统为 Linux &#xff1b…