转载

MyBatis代码整洁之道之设计模式

到目前为止, MyBatis 的源代码也看的七七八八了,不得不说 MyBatis 的代码写的真的很不错,逻辑清晰的同时功能还很齐全,同时可以发现, MyBatis 中使用了非常多的设计模式,接下来简单总结下:

建造者模式

在 MyBaits 的各种对象构建类中,大多数都继承自 BaseBuilder 类,并且大多数类都是通过建造者模式实现。

比如:

  • SqlSessionFactoryBuilder 构建 SqlSessionFactory
  • XMLStatementBuilder 构建 XMLStatement
  • SqlSourceBuilder 构建 SqlSource
  • CacheBuilder 构建 Cache

这些类都有一个共同点,那就是所创建的对象的过程比较复杂,就比如 SqlSessionFactory 的创建,是需要解析整个配置文件。

因此,他们将对象的创建与使用分离开来,使用单独的建造者类来创建这个对象。

同时,对于 CacheBuilder 这种具有可选参数的类, MyBatis 还是用了经典的连点设计:

Cache cache = new CacheBuilder(currentNamespace)
    .implementation(valueOrDefault(typeClass, PerpetualCache.class))
    .addDecorator(valueOrDefault(evictionClass, LruCache.class))
    .clearInterval(flushInterval)
    .size(size)
    .readWrite(readWrite)
    .blocking(blocking)
    .properties(props)
    .build();

因此,对于以后我们的代码中,如果创建一个对象非常复杂,则可以选择使用建造者模式将对象的创建和使用分离开来。

工厂模式

MyBatis 中也存在很多 Factory ,主要是用来根据不同的参数初始化对象。

比如:

  • SqlSessionFactory 创建 SqlSession
  • ProxyFactory 创建代理类
  • LogFactory 获取用户当前配置的 Log
  • DefaultReflectorFactory 创建 Reflector

这些类的作用都是为了在创建对象前/后进行一些额外的处理,或者是修改获取这个类的获取方式,比如 DefaultReflectorFactory 是为了添加一层缓存,创建的时候首先读取缓存,比如 LogFactory 是为了查找是否有用户自己配置的 Log ,如果有,则使用用户配置类等。

这里也可以看出来, 建造者模式和工厂模式的区别在于建造者模式主要用于解决创建对象的复杂问题,而工厂模式是为了简单修改获取对象的方式以及根据用户的参数创建不同对象

但是其实两者都是为了将对象的创建和使用分开。

同时值得一提的是, MyBatis 这里都是使用的工厂模式,而不是简单的简单工厂,这样使得代码根据低耦合,同时可以让用户自己实习这些工厂类。

单例模式

说实话, MyBatis 中单例模式用的还是比较少,因为使用单例就有点像是在使用静态类。不过对于一些简单的工具类, MyBatis 依然使用了单例模式。其中创建单例模式的方式使用的是最方便的静态内部类创建法。

比如:

  • VFS 类

适配器模式

在 MyBatis 中,可以自己定义 Log 的实现,使得整个集成 MyBatis 的系统可以使用同一的日志系统。然而对于多种 Log 的实现, MyBatis 想要使用一个接口统一的使用它,就需要用到适配器模式了。

在 MyBatis 的 Log 包中,定义了一个 Log 接口,然后将各种其他 Log 都实现了这个 Log 接口,比如

JakartaCommonsLoggingImpl
Jdk14LoggingImpl
Log4jImpl
Log4j2Impl

这样,在 MyBatis 中就可以无差别的使用这些实现类

代理模式

一般来说,我们用代理模式的情况很少,但是相信动态代理大家一定听说过。

由于 Java 提供的动态类实在方便并且功能强大,所以很多地方都会有它的身影。

比如:

Mapper
SQL

装饰器模式

MyBatis 的 Cache 可以被用户定义很多不同的特性,被缓存替换规则,是否有定时刷新,使用弱引用引用对象等等。。

刚开始接触这些配置的时候,就觉得如果让我设计,还挺麻烦,多么多功能。

后来看源码才发现设计的巧妙, MyBatis 直接使用装饰器模式将 HashMap 层层封装,需要什么功能就添加一层包装,这样在后面的功能扩展中,完全不用修改任何的代码,真是将装饰器模式用到了淋漓尽致。

组合模式

MyBatis 的动态 SQL 中,可以定义一些类似编程语言结构。并且 MyBatis 允许嵌套定义这种结构,比如:

<update id="update" parameterType="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> 1=1
     <if test="id != null">
       and id = ${id}
     </if>
   </where>
</update>

这种类似一个树型结构。

恰好,组合模式便是用来针对这种情况的,使用组合模式,能够非常方便的遍历以及处理整个树的所有节点。并且不用各种 if-else 的判断

模板方法模式

模板方法模式也是我们日常开发中比较喜欢使用的模式,它比较简单,而且能够有效地消除重复代码,在 MyBatis 中,有很多 BaseXX ,目的就是为了使用模板方法模式

比如:

BaseExecutor
BaseStatementHandler
BaseTypeHandler

模板方法很大限度的使用了继承的特性,它将不变的代码放在父类,变化留给子类,重用了代码,并且使代码的使用顺序也被固定在子类当中。

责任链模式

在 MyBatis 中,用户可以自定义插件修改一些核心类的行为,而实现方式便是通过责任链。

MyBatis 使用 InterceptorChain 保存所有插件,通过动态代理将插件包装成需要使用的目标类,完美的实现的插件的功能。

不过在使用插件的过程中,需要注意调用 invocation#proceed() ,否则可能会出现断链的情况。

策略模式

MyBatis 中,可以自定义使用三种不同的 Executor ,也可以自定义使用三种不同的 Statement 。

MyBatis 的处理方式便是将这些类通过3种不同的类实现,并实现同一个接口。然后通过依赖注入的方式注入到使用类中。

这便是策略模式,策略模式使用非常简单,其实就是对依赖注入的实施。

策略模式一般都会和简单工厂配合使用,因为需要根据条件,选择不同的策略。

迭代器模式

在使用 MyBatis 定义 SQL 的时候,可以使用 #{} 获取参数,同时还可以使用 #{xxx.xx} 获取对象的属性,同时还可以使用 #{xxx[1]} 获取集合中的指定坐标的元素。

这么多种访问方式,并且还可以嵌套使用,

比如:

student.names[0].firstName

在 MyBatis 中,负责解析这种语法的类为 PropertyTokenizer ,这个类实现了 Iterator 接口,使得其他代码可以使用迭代器来访问它。

但是,在 MyBatis 中,并没有使用 foreach 语法糖来访问它,而是直接使用了 hasNext() , next() 方法结合递归进行访问。

—

总结

到这里, MyBatis 中所使用的设计模式基本总结完毕,可以看出来, MyBatis 结合自身的业务逻辑,发挥了设计模式的巨大威力。使得我们从命名上就能大概明白内部逻辑,同时也使得整体的代码简洁可维护,这值得我们好好学习。

原文  http://dengchengchao.com/?p=1218
正文到此结束
Loading...