设计模式学习10----建造者模式

前言

这些天在阅读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();

如上,是一个对建造者模式的最佳应用。其中

CacheBuilder 类充当了具体建造者的角色
Cache 类充当了具体产品的角色
MapperBuilderAssistant 充当了指挥者的角色。

跟标准建造者模式不同的是,Cache 对象是由反射生成的。不是直接实例化得到的。
下面,我们画个类图更好的理解下。

总结

本文,首先对建造者模式的基本结构和角色进行了阐述,是读者对建造者有个大致的理解。然后,简要的介绍了建造者模式的优缺点。最后,通过一个例子简单介绍了建造者模式在MyBatis中的实际运用。希望本文对读者朋友们有所帮助。




作者:码农飞哥
微信公众号:码农飞哥