前言
这些天在阅读MyBatis的源码,发现MyBatis源码中运用了很多设计模式,例如:模板模式,建造者模式,装饰器模式。其中最常用的就是建造者模式。下面我们就来学习下建造者模式。
建造者模式
建造者模式结构图及角色
建造者模式中的主要角色如下:
建造者(Builder)接口:Builder接口用于定义构建产品对象的各部分的行为。
具体建造者(ConcreteBuilder)角色:直接创建产品对象的具体建造者。具体建造者类必须实现建造者接口所要求的两类方法:一类是建造方法,如上图中的 buildPart1()等方法。另外一类是获取构造好的产品对象的方法,如上图中的getProduct()方法。
指挥者(Director)角色: 该角色会通过调用具体建造者,创建需要的产品对象
产品(Product)角色:一个具体的产品对象
建造者模式的优缺点
优点
将复杂产品的创建步骤分解在不同的方法中。使得创建过程更加清晰,使得我们能更加精确的控制复杂对象的产生过程。
将产品的创建过程与产品本身分离开来,可以使用相同的创建过程来得到不同的产品。也就是说细节依赖抽象
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或者增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异很大,则不适合使用建造者模式。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
MyBatis中应用建造者模式
在MyBatis中应用到建造者模式的地方有很多,我这里举一个用的最多的点。在解析映射文件中的cache时,创建缓存构造器运用到了建造者模式。代码如下:
public class CacheBuilder { private String id; private Class<? extends Cache> implementation; private List<Class<? extends Cache>> decorators; private Integer size; private Long clearInterval; private boolean readWrite; private Properties properties; private boolean blocking; public CacheBuilder(String id) { this.id = id; this.decorators = new ArrayList<Class<? extends Cache>>(); } public CacheBuilder implementation(Class<? extends Cache> implementation) { this.implementation = implementation; return this; } public CacheBuilder addDecorator(Class<? extends Cache> decorator) { if (decorator != null) { this.decorators.add(decorator); } return this; } public CacheBuilder size(Integer size) { this.size = size; return this; } public CacheBuilder clearInterval(Long clearInterval) { this.clearInterval = clearInterval; return this; } public CacheBuilder readWrite(boolean readWrite) { this.readWrite = readWrite; return this; } public CacheBuilder blocking(boolean blocking) { this.blocking = blocking; return this; } public CacheBuilder properties(Properties properties) { this.properties = properties; return this; } /** * @return */ public Cache build() { // 1. 设置默认的缓存类型(PerpetualCache)和缓存装饰器(LruCache) setDefaultImplementations(); //通过反射创建缓存 Cache cache = newBaseCacheInstance(implementation, id); //设额外属性,初始化Cache对象 setCacheProperties(cache); // issue #352, do not apply decorators to custom caches // 2. 仅对内置缓存PerpetualCache应用装饰器 if (PerpetualCache.class.equals(cache.getClass())) { for (Class<? extends Cache> decorator : decorators) { //装饰者模式一个个包装cache cache = newCacheDecoratorInstance(decorator, cache); //又要来一遍设额外属性 setCacheProperties(cache); } //3. 应用标准的装饰者,比如LoggingCache,SynchronizedCache cache = setStandardDecorators(cache); } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) { //4.如果是custom缓存,且不是日志,要加日志 cache = new LoggingCache(cache); } return cache; } private void setDefaultImplementations() { //又是一重保险,如果为null则设默认值, // 虽然和XMLMapperBuilder.cacheElement // 以及MapperBuilderAssistant.useNewCache逻辑重复了,但是还是有必要 //如果用户忘记设置implementation或者人为的将implementation设为空,会导致 //build方法在构建实例时触发空指针异常。 if (implementation == null) { // 设置默认的缓存实现类 implementation = PerpetualCache.class; if (decorators.isEmpty()) { // 添加LruCache装饰器 decorators.add(LruCache.class); } } } //最后附加上标准的装饰者 private Cache setStandardDecorators(Cache cache) { try { // 创建"元信息"对象 MetaObject metaCache = SystemMetaObject.forObject(cache); if (size != null && metaCache.hasSetter("size")) { metaCache.setValue("size", size); } if (clearInterval != null) { //刷新缓存间隔,怎么刷新呢,用ScheduledCache来刷,还是装饰者模式,漂亮! cache = new ScheduledCache(cache); ((ScheduledCache) cache).setClearInterval(clearInterval); } if (readWrite) { //如果readOnly=false,可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。 cache = new SerializedCache(cache); } //日志缓存 cache = new LoggingCache(cache); //同步缓存, 3.2.6以后这个类已经没用了,考虑到Hazelcast, EhCache已经有锁机制了,所以这个锁就画蛇添足了。 cache = new SynchronizedCache(cache); if (blocking) { cache = new BlockingCache(cache); } return cache; } catch (Exception e) { throw new CacheException("Error building standard cache decorators. Cause: " + e, e); } } //省略部分代码 }
我们再来看看调用者。
// * MapperBuilderAssistant //调用CacheBuilder构建cache,id=currentNamespace(使用建造者模式构建缓存实例) Cache cache = new CacheBuilder(currentNamespace) .implementation(typeClass) .addDecorator(evictionClass) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build();
如上,是一个对建造者模式的最佳应用。其中
1.CacheBuilder 类充当了具体建造者的角色
2.Cache 类充当了具体产品的角色
3.MapperBuilderAssistant 充当了指挥者的角色。
跟标准建造者模式不同的是,Cache 对象是由反射生成的。不是直接实例化得到的。
下面,我们画个类图更好的理解下。
总结
本文,首先对建造者模式的基本结构和角色进行了阐述,是读者对建造者有个大致的理解。然后,简要的介绍了建造者模式的优缺点。最后,通过一个例子简单介绍了建造者模式在MyBatis中的实际运用。希望本文对读者朋友们有所帮助。