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