設(shè)計(jì)模式學(xué)習(xí)11----裝飾者模式
定義
裝飾者模式也稱(chēng)為包裝模式(Wrapper Pattern),屬于結(jié)構(gòu)型設(shè)計(jì)模式。
在不改變?cè)?lèi)文件以及不使用繼承的情況下,動(dòng)態(tài)地將責(zé)任附加到對(duì)象中,從而實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)展一個(gè)對(duì)象的功能。它通過(guò)創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾來(lái)包裹真實(shí)對(duì)象。
結(jié)構(gòu)類(lèi)圖
角色
抽象組件(Component): 定義裝飾方法的規(guī)范
被裝飾者(ConcreteComponent): Component的具體實(shí)現(xiàn),也就是我們要裝飾的具體對(duì)象
裝飾者組件(Decorator): 持有組件(Component)對(duì)象的實(shí)例引用,該類(lèi)的職責(zé)就是為了裝飾具體組件對(duì)象,定義的規(guī)范。
具體裝飾(ConcreteDecorator): 負(fù)責(zé)給構(gòu)件對(duì)象裝飾附加的功能
裝飾者模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
把類(lèi)匯總的裝飾功能從類(lèi)中搬出,擴(kuò)展性十分良好
把類(lèi)中的核心職責(zé)和裝飾功能區(qū)分開(kāi)來(lái),結(jié)構(gòu)清晰明了,并且可以去除相關(guān)類(lèi)的重復(fù)裝飾邏輯,靈活性好
缺點(diǎn)
會(huì)出現(xiàn)很多小類(lèi),即裝飾類(lèi)。
MyBatis中應(yīng)用裝飾模式
在MyBatis中有一級(jí)和二級(jí)緩存。 在BaseExecutor中,存放著一級(jí)緩存,org.apache.ibatis.cache.impl.PerpetualCache 是默認(rèn)的實(shí)現(xiàn)
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
//本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢(xún)(一級(jí)緩存)
//本地緩存
protected PerpetualCache localCache;
//本地輸出參數(shù)緩存
protected PerpetualCache localOutputParameterCache;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue();
this.localCache = new PerpetualCache(“LocalCache”);
this.localOutputParameterCache = new PerpetualCache(“LocalOutputParameterCache”);
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
//省略其他代碼
而當(dāng)我們初始化時(shí),會(huì)對(duì)PerpetualCache 進(jìn)行包裝,查看CacheBuilder 我們就可以看出。
MyBatis 一級(jí)緩存結(jié)構(gòu)圖
如上結(jié)構(gòu)圖所示:
Cache 作為抽象組件定義了存取值的相關(guān)方法
PerpetualCache 作為具體被裝飾者,實(shí)現(xiàn)了Cache里的相關(guān)方法
LruCache 等作為具體的裝飾者,持有了Cache對(duì)象的實(shí)例引用。
代碼解析
CacheBuilder 類(lèi)的build方法。CacheBuilder 作為客戶(hù)端調(diào)用類(lèi)。
public Cache build() {
// 1. 設(shè)置默認(rèn)的緩存類(lèi)型(PerpetualCache)和緩存裝飾器(LruCache)
setDefaultImplementations();
//通過(guò)反射創(chuàng)建緩存
Cache cache = newBaseCacheInstance(implementation, id);
//設(shè)額外屬性,初始化Cache對(duì)象
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
// 2. 僅對(duì)內(nèi)置緩存PerpetualCache應(yīng)用裝飾器
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
//裝飾者模式一個(gè)個(gè)包裝cache
cache = newCacheDecoratorInstance(decorator, cache);
//又要來(lái)一遍設(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 Cache newCacheDecoratorInstance(Class<? extends Cache> cacheClass, Cache base) {
Constructor<? extends Cache> cacheConstructor = getCacheDecoratorConstructor(cacheClass);
try {
return cacheConstructor.newInstance(base);
} catch (Exception e) {
throw new CacheException("Could not instantiate cache decorator (" + cacheClass + "). Cause: " + e, e);
}
}
//最后附加上標(biāo)準(zhǔn)的裝飾者
- 1
private Cache setStandardDecorators(Cache cache) {
try {
// 創(chuàng)建"元信息"對(duì)象
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter(“size”)) {
metaCache.setValue(“size”, size);
}
if (clearInterval != null) {
//刷新緩存間隔,怎么刷新呢,用ScheduledCache來(lái)刷,還是裝飾者模式,漂亮!
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) {
//如果readOnly=false,可讀寫(xiě)的緩存 會(huì)返回緩存對(duì)象的拷貝(通過(guò)序列化) 。這會(huì)慢一些,但是安全,因此默認(rèn)是 false。
cache = new SerializedCache(cache);
}
//日志緩存
cache = new LoggingCache(cache);
//同步緩存, 3.2.6以后這個(gè)類(lèi)已經(jīng)沒(méi)用了,考慮到Hazelcast, EhCache已經(jīng)有鎖機(jī)制了,所以這個(gè)鎖就畫(huà)蛇添足了。
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);
}
}
接著我們來(lái)看看具體被裝飾者(PerpetualCache)類(lèi)
/**
- 永久緩存
- 一旦存入就一直保持
*/
public class PerpetualCache implements Cache {
//每個(gè)永久緩存有一個(gè)ID來(lái)識(shí)別
- 1
private String id;
//內(nèi)部就是一個(gè)HashMap,所有方法基本就是直接調(diào)用HashMap的方法,不支持多線程?
private Map<Object, Object> cache = new HashMap<Object, Object>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
}
PerpetualCache 類(lèi)很簡(jiǎn)單,我們就不做詳細(xì)分析了。接著我們來(lái)看看具體裝飾者LruCache類(lèi),該裝飾器類(lèi)主要作用是移除最近最少使用的緩存。
/*
- 最近最少使用緩存
- 基于 LinkedHashMap 覆蓋其 removeEldestEntry 方法實(shí)現(xiàn)。
*/
public class LruCache implements Cache {
private final Cache delegate;
//額外用了一個(gè)map才做lru,但是委托的Cache里面其實(shí)也是一個(gè)map,這樣等于用2倍的內(nèi)存實(shí)現(xiàn)lru功能
private Map<Object, Object> keyMap;
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
//核心就是覆蓋 LinkedHashMap.removeEldestEntry方法,
//返回true或false告訴 LinkedHashMap要不要?jiǎng)h除此最老鍵值
//LinkedHashMap內(nèi)部其實(shí)就是每次訪問(wèn)或者插入一個(gè)元素都會(huì)把元素放到鏈表末尾,
//這樣不經(jīng)常訪問(wèn)的鍵值肯定就在鏈表開(kāi)頭啦
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
//這里沒(méi)轍了,把eldestKey存入實(shí)例變量
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
@Override
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
//增加新紀(jì)錄后,判斷是否要將最老元素移除
cycleKeyList(key);
}
@Override
public Object getObject(Object key) {
//get的時(shí)候調(diào)用一下LinkedHashMap.get,讓經(jīng)常訪問(wèn)的值移動(dòng)到鏈表末尾
keyMap.get(key); //touch
return delegate.getObject(key);
}
}
總結(jié)
本文簡(jiǎn)單的介紹了裝飾者模式,裝飾者模式也是一種比較常用的模式。主要運(yùn)用在需要給客戶(hù)裝飾很多特性時(shí),例如,給人穿衣服就一個(gè)很好的裝飾模式應(yīng)用場(chǎng)景。
作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥
