設(shè)計模式學(xué)習(xí)11----裝飾者模式
定義
裝飾者模式也稱為包裝模式(Wrapper Pattern),屬于結(jié)構(gòu)型設(shè)計模式。
在不改變原類文件以及不使用繼承的情況下,動態(tài)地將責(zé)任附加到對象中,從而實現(xiàn)動態(tài)擴(kuò)展一個對象的功能。它通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實對象。
結(jié)構(gòu)類圖
角色
抽象組件(Component): 定義裝飾方法的規(guī)范
被裝飾者(ConcreteComponent): Component的具體實現(xiàn),也就是我們要裝飾的具體對象
裝飾者組件(Decorator): 持有組件(Component)對象的實例引用,該類的職責(zé)就是為了裝飾具體組件對象,定義的規(guī)范。
具體裝飾(ConcreteDecorator): 負(fù)責(zé)給構(gòu)件對象裝飾附加的功能
裝飾者模式的優(yōu)缺點
優(yōu)點
把類匯總的裝飾功能從類中搬出,擴(kuò)展性十分良好
把類中的核心職責(zé)和裝飾功能區(qū)分開來,結(jié)構(gòu)清晰明了,并且可以去除相關(guān)類的重復(fù)裝飾邏輯,靈活性好
缺點
會出現(xiàn)很多小類,即裝飾類。
MyBatis中應(yīng)用裝飾模式
在MyBatis中有一級和二級緩存。 在BaseExecutor中,存放著一級緩存,org.apache.ibatis.cache.impl.PerpetualCache 是默認(rèn)的實現(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ù)嵌套查詢(一級緩存)
//本地緩存
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)我們初始化時,會對PerpetualCache 進(jìn)行包裝,查看CacheBuilder 我們就可以看出。
MyBatis 一級緩存結(jié)構(gòu)圖
如上結(jié)構(gòu)圖所示:
Cache 作為抽象組件定義了存取值的相關(guān)方法
PerpetualCache 作為具體被裝飾者,實現(xiàn)了Cache里的相關(guān)方法
LruCache 等作為具體的裝飾者,持有了Cache對象的實例引用。
代碼解析
CacheBuilder 類的build方法。CacheBuilder 作為客戶端調(diào)用類。
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) {
//裝飾者模式一個個包裝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 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)建"元信息"對象
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以后這個類已經(jīng)沒用了,考慮到Hazelcast, EhCache已經(jīng)有鎖機(jī)制了,所以這個鎖就畫蛇添足了。
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);
}
}
接著我們來看看具體被裝飾者(PerpetualCache)類
/**
- 永久緩存
- 一旦存入就一直保持
*/
public class PerpetualCache implements Cache {
//每個永久緩存有一個ID來識別
- 1
private String id;
//內(nèi)部就是一個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 類很簡單,我們就不做詳細(xì)分析了。接著我們來看看具體裝飾者LruCache類,該裝飾器類主要作用是移除最近最少使用的緩存。
/*
- 最近最少使用緩存
- 基于 LinkedHashMap 覆蓋其 removeEldestEntry 方法實現(xiàn)。
*/
public class LruCache implements Cache {
private final Cache delegate;
//額外用了一個map才做lru,但是委托的Cache里面其實也是一個map,這樣等于用2倍的內(nèi)存實現(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要不要刪除此最老鍵值
//LinkedHashMap內(nèi)部其實就是每次訪問或者插入一個元素都會把元素放到鏈表末尾,
//這樣不經(jīng)常訪問的鍵值肯定就在鏈表開頭啦
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
//這里沒轍了,把eldestKey存入實例變量
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的時候調(diào)用一下LinkedHashMap.get,讓經(jīng)常訪問的值移動到鏈表末尾
keyMap.get(key); //touch
return delegate.getObject(key);
}
}
總結(jié)
本文簡單的介紹了裝飾者模式,裝飾者模式也是一種比較常用的模式。主要運用在需要給客戶裝飾很多特性時,例如,給人穿衣服就一個很好的裝飾模式應(yīng)用場景。
作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥
