設(shè)計(jì)模式學(xué)習(xí)10----建造者模式

前言

這些天在閱讀MyBatis的源碼,發(fā)現(xiàn)MyBatis源碼中運(yùn)用了很多設(shè)計(jì)模式,例如:模板模式,建造者模式,裝飾器模式。其中最常用的就是建造者模式。下面我們就來學(xué)習(xí)下建造者模式。
建造者模式
建造者模式結(jié)構(gòu)圖及角色
在這里插入圖片描述

建造者模式中的主要角色如下:

建造者(Builder)接口:Builder接口用于定義構(gòu)建產(chǎn)品對象的各部分的行為。
具體建造者(ConcreteBuilder)角色:直接創(chuàng)建產(chǎn)品對象的具體建造者。具體建造者類必須實(shí)現(xiàn)建造者接口所要求的兩類方法:一類是建造方法,如上圖中的 buildPart1()等方法。另外一類是獲取構(gòu)造好的產(chǎn)品對象的方法,如上圖中的getProduct()方法。
指揮者(Director)角色: 該角色會通過調(diào)用具體建造者,創(chuàng)建需要的產(chǎn)品對象
產(chǎn)品(Product)角色:一個(gè)具體的產(chǎn)品對象

建造者模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)

將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中。使得創(chuàng)建過程更加清晰,使得我們能更加精確的控制復(fù)雜對象的產(chǎn)生過程。
將產(chǎn)品的創(chuàng)建過程與產(chǎn)品本身分離開來,可以使用相同的創(chuàng)建過程來得到不同的產(chǎn)品。也就是說細(xì)節(jié)依賴抽象
每一個(gè)具體建造者都相對獨(dú)立,而與其他的具體建造者無關(guān),因此可以很方便地替換具體建造者或者增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。

缺點(diǎn)

建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似,如果產(chǎn)品之間差異很大,則不適合使用建造者模式。
如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大。

MyBatis中應(yīng)用建造者模式

在MyBatis中應(yīng)用到建造者模式的地方有很多,我這里舉一個(gè)用的最多的點(diǎn)。在解析映射文件中的cache時(shí),創(chuàng)建緩存構(gòu)造器運(yùn)用到了建造者模式。代碼如下:

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. 設(shè)置默認(rèn)的緩存類型(PerpetualCache)和緩存裝飾器(LruCache)
    setDefaultImplementations();
    //通過反射創(chuàng)建緩存
    Cache cache = newBaseCacheInstance(implementation, id);
    //設(shè)額外屬性,初始化Cache對象
    setCacheProperties(cache);
    // issue #352, do not apply decorators to custom caches
    // 2. 僅對內(nèi)置緩存PerpetualCache應(yīng)用裝飾器
    if (PerpetualCache.class.equals(cache.getClass())) {
    for (Class<? extends Cache> decorator : decorators) {
    //裝飾者模式一個(gè)個(gè)包裝cache
    cache = newCacheDecoratorInstance(decorator, cache);
    //又要來一遍設(shè)額外屬性
    setCacheProperties(cache);
    }
    //3. 應(yīng)用標(biāo)準(zhǔn)的裝飾者,比如LoggingCache,SynchronizedCache
    cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
    //4.如果是custom緩存,且不是日志,要加日志
    cache = new LoggingCache(cache);
    }
    return cache;
    }
    private void setDefaultImplementations() {
    //又是一重保險(xiǎn),如果為null則設(shè)默認(rèn)值,
    // 雖然和XMLMapperBuilder.cacheElement
    // 以及MapperBuilderAssistant.useNewCache邏輯重復(fù)了,但是還是有必要
    //如果用戶忘記設(shè)置implementation或者人為的將implementation設(shè)為空,會導(dǎo)致
    //build方法在構(gòu)建實(shí)例時(shí)觸發(fā)空指針異常。
    if (implementation == null) {
    // 設(shè)置默認(rèn)的緩存實(shí)現(xiàn)類
    implementation = PerpetualCache.class;
    if (decorators.isEmpty()) {
    // 添加LruCache裝飾器
    decorators.add(LruCache.class);
    }
    }
    }

//最后附加上標(biāo)準(zhǔn)的裝飾者
private Cache setStandardDecorators(Cache cache) {
try {
// 創(chuàng)建"元信息"對象
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,可讀寫的緩存 會返回緩存對象的拷貝(通過序列化) 。這會慢一些,但是安全,因此默認(rèn)是 false。
cache = new SerializedCache(cache);
}
//日志緩存
cache = new LoggingCache(cache);
//同步緩存, 3.2.6以后這個(gè)類已經(jīng)沒用了,考慮到Hazelcast, EhCache已經(jīng)有鎖機(jī)制了,所以這個(gè)鎖就畫蛇添足了。
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);
}
}
//省略部分代碼
}

我們再來看看調(diào)用者。

// * MapperBuilderAssistant
//調(diào)用CacheBuilder構(gòu)建cache,id=currentNamespace(使用建造者模式構(gòu)建緩存實(shí)例)
Cache cache = new CacheBuilder(currentNamespace)
    .implementation(typeClass)
    .addDecorator(evictionClass)
    .clearInterval(flushInterval)
    .size(size)
    .readWrite(readWrite)
    .blocking(blocking)
    .properties(props)
    .build();

如上,是一個(gè)對建造者模式的最佳應(yīng)用。其中

CacheBuilder 類充當(dāng)了具體建造者的角色
Cache 類充當(dāng)了具體產(chǎn)品的角色
MapperBuilderAssistant 充當(dāng)了指揮者的角色。

跟標(biāo)準(zhǔn)建造者模式不同的是,Cache 對象是由反射生成的。不是直接實(shí)例化得到的。
下面,我們畫個(gè)類圖更好的理解下。

總結(jié)

本文,首先對建造者模式的基本結(jié)構(gòu)和角色進(jìn)行了闡述,是讀者對建造者有個(gè)大致的理解。然后,簡要的介紹了建造者模式的優(yōu)缺點(diǎn)。最后,通過一個(gè)例子簡單介紹了建造者模式在MyBatis中的實(shí)際運(yùn)用。希望本文對讀者朋友們有所幫助。




作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥