設(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)飛哥
