设计模式学习11----装饰者模式
【摘要】 定义
装饰者模式也称为包装模式(Wrapper Pattern),属于结构型设计模式。 在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象中,从而实现动态扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象。
结构类图
角色
抽象组件(Component): 定义装饰方法的规范被装饰者(ConcreteComponent): Co...
定义
装饰者模式也称为包装模式(Wrapper Pattern),属于结构型设计模式。
在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象中,从而实现动态扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象。
结构类图
角色
- 抽象组件(Component): 定义装饰方法的规范
- 被装饰者(ConcreteComponent): Component的具体实现,也就是我们要装饰的具体对象
- 装饰者组件(Decorator): 持有组件(Component)对象的实例引用,该类的职责就是为了装饰具体组件对象,定义的规范。
- 具体装饰(ConcreteDecorator): 负责给构件对象装饰附加的功能
装饰者模式的优缺点
优点
- 把类汇总的装饰功能从类中搬出,扩展性十分良好
- 把类中的核心职责和装饰功能区分开来,结构清晰明了,并且可以去除相关类的重复装饰逻辑,灵活性好
缺点
- 会出现很多小类,即装饰类。
MyBatis中应用装饰模式
在MyBatis中有一级和二级缓存。 在BaseExecutor中,存放着一级缓存,org.apache.ibatis.cache.impl.PerpetualCache 是默认的实现
public abstract class BaseExecutor implements Executor { private static final Log log = LogFactory.getLog(BaseExecutor.class); protected Transaction transaction;
protected Executor wrapper; //本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询(一级缓存)
//本地缓存
protected PerpetualCache localCache;
//本地输出参数缓存
protected PerpetualCache localOutputParameterCache; protected BaseExecutor(Configuration configuration, Transaction transaction) { this.transaction = transaction; this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>(); this.localCache = new PerpetualCache("LocalCache"); this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this;
}
//省略其他代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
而当我们初始化时,会对PerpetualCache 进行包装,查看CacheBuilder 我们就可以看出。
MyBatis 一级缓存结构图
如上结构图所示:
- Cache 作为抽象组件定义了存取值的相关方法
- PerpetualCache 作为具体被装饰者,实现了Cache里的相关方法
- LruCache 等作为具体的装饰者,持有了Cache对象的实例引用。
代码解析
CacheBuilder 类的build方法。CacheBuilder 作为客户端调用类。
public Cache build() {
// 1. 设置默认的缓存类型(PerpetualCache)和缓存装饰器(LruCache) setDefaultImplementations(); //通过反射创建缓存 Cache cache = newBaseCacheInstance(implementation, id); //设额外属性,初始化Cache对象 setCacheProperties(cache); // issue #352, do not apply decorators to custom caches
// 2. 仅对内置缓存PerpetualCache应用装饰器 if (PerpetualCache.class.equals(cache.getClass())) { for (Class<? extends Cache> decorator : decorators) { //装饰者模式一个个包装cache cache = newCacheDecoratorInstance(decorator, cache); //又要来一遍设额外属性 setCacheProperties(cache); } //3. 应用标准的装饰者,比如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); }
} //最后附加上标准的装饰者
private Cache setStandardDecorators(Cache cache) { try {
// 创建"元信息"对象 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,可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。 cache = new SerializedCache(cache); } //日志缓存 cache = new LoggingCache(cache); //同步缓存, 3.2.6以后这个类已经没用了,考虑到Hazelcast, EhCache已经有锁机制了,所以这个锁就画蛇添足了。 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); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
接着我们来看看具体被装饰者(PerpetualCache)类
/**
* 永久缓存
* 一旦存入就一直保持
*
*/
public class PerpetualCache implements Cache { //每个永久缓存有一个ID来识别
private String id; //内部就是一个HashMap,所有方法基本就是直接调用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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
PerpetualCache 类很简单,我们就不做详细分析了。接着我们来看看具体装饰者LruCache类,该装饰器类主要作用是移除最近最少使用的缓存。
/*
* 最近最少使用缓存
* 基于 LinkedHashMap 覆盖其 removeEldestEntry 方法实现。
*/
public class LruCache implements Cache { private final Cache delegate;
//额外用了一个map才做lru,但是委托的Cache里面其实也是一个map,这样等于用2倍的内存实现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内部其实就是每次访问或者插入一个元素都会把元素放到链表末尾, //这样不经常访问的键值肯定就在链表开头啦 @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); //增加新纪录后,判断是否要将最老元素移除 cycleKeyList(key);
} @Override
public Object getObject(Object key) { //get的时候调用一下LinkedHashMap.get,让经常访问的值移动到链表末尾 keyMap.get(key); //touch return delegate.getObject(key);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
总结
本文简单的介绍了装饰者模式,装饰者模式也是一种比较常用的模式。主要运用在需要给客户装饰很多特性时,例如,给人穿衣服就一个很好的装饰模式应用场景。
文章来源: feige.blog.csdn.net,作者:码农飞哥,版权归原作者所有,如需转载,请联系作者。
原文链接:feige.blog.csdn.net/article/details/89857768
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)