设计模式学习11----装饰者模式

举报
码农飞哥 发表于 2021/05/29 11:34:53 2021/05/29
【摘要】 定义 装饰者模式也称为包装模式(Wrapper Pattern),属于结构型设计模式。 在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象中,从而实现动态扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象。 结构类图 角色 抽象组件(Component): 定义装饰方法的规范被装饰者(ConcreteComponent): Co...

定义

装饰者模式也称为包装模式(Wrapper Pattern),属于结构型设计模式。
在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象中,从而实现动态扩展一个对象的功能。它通过创建一个包装对象,也就是装饰来包裹真实对象。

结构类图

在这里插入图片描述

角色

  1. 抽象组件(Component): 定义装饰方法的规范
  2. 被装饰者(ConcreteComponent): Component的具体实现,也就是我们要装饰的具体对象
  3. 装饰者组件(Decorator): 持有组件(Component)对象的实例引用,该类的职责就是为了装饰具体组件对象,定义的规范。
  4. 具体装饰(ConcreteDecorator): 负责给构件对象装饰附加的功能

装饰者模式的优缺点

优点

  1. 把类汇总的装饰功能从类中搬出,扩展性十分良好
  2. 把类中的核心职责和装饰功能区分开来,结构清晰明了,并且可以去除相关类的重复装饰逻辑,灵活性好

缺点

  1. 会出现很多小类,即装饰类。

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 一级缓存结构图

在这里插入图片描述
如上结构图所示:

  1. Cache 作为抽象组件定义了存取值的相关方法
  2. PerpetualCache 作为具体被装饰者,实现了Cache里的相关方法
  3. 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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。