23个设计模式学习笔记:06.装饰模式

装饰模式

  • “单一职责”模式
      • 典型模式
  • 动机(Motivation)
  • 模式定义
  • 结构(Structure)
  • 要点总结
  • 代码示例
  • 参考资料

“单一职责”模式

在软件组件的设计中,如果职责划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任

典型模式

  • 装饰模式(Decorator)
  • 桥接模式(Bridge)

首先介绍装饰模式

动机(Motivation)

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性,并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀
  • 如何使“对象功能的扩展”能够根据需求来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

模式定义

动态(组合)地给一个对象增加一些额外的职责,就增加功能而言,装饰模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)

结构(Structure)

23个设计模式学习笔记:06.装饰模式_第1张图片

要点总结

  • 通过采用组合而非继承的手法,装饰模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生问题”
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口,但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类
  • 装饰模式的目的并非解决“多子类衍生的多继承问题”,装饰模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义

代码示例

Mybatis中缓存模块的实现应用到了装饰模式,我们把相关代码提取出来做一个简单的分析,首先我们把缓存模块的代码结构和类继承结构图展示如下,便于理解:

  • Mybatis缓存模块代码结构图
    23个设计模式学习笔记:06.装饰模式_第2张图片
  • Mybatis缓存模块类继承结构图
    23个设计模式学习笔记:06.装饰模式_第3张图片
public interface Cache {

  /**
   * @return The identifier of this cache
   */
  String getId();

  /**
   * @param key Can be any object but usually it is a {@link CacheKey}
   * @param value The result of a select.
   */
  void putObject(Object key, Object value);

  /**
   * @param key The key
   * @return The object stored in the cache.
   */
  Object getObject(Object key);

  /**
   * As of 3.3.0 this method is only called during a rollback
   * for any previous value that was missing in the cache.
   * This lets any blocking cache to release the lock that
   * may have previously put on the key.
   * A blocking cache puts a lock when a value is null
   * and releases it when the value is back again.
   * This way other threads will wait for the value to be
   * available instead of hitting the database.
   *
   *
   * @param key The key
   * @return Not used
   */
  Object removeObject(Object key);

  /**
   * Clears this cache instance.
   */
  void clear();

  /**
   * Optional. This method is not called by the core.
   *
   * @return The number of elements stored in the cache (not its capacity).
   */
  int getSize();

  /**
   * Optional. As of 3.2.6 this method is no longer called by the core.
   * 

* Any locking needed by the cache must be provided internally by the cache provider. * * @return A ReadWriteLock */ default ReadWriteLock getReadWriteLock() { return null; } } public class PerpetualCache implements Cache { private final String id; private final Map cache = new HashMap<>(); public PerpetualCache(String id) { this.id = id; } @Override public String getId() { return id; } @Override public int getSize() { return cache.size(); } @Override public void putObject(Object key, Object value) { cache.put(key, value); } @Override public Object getObject(Object key) { return cache.get(key); } @Override public Object removeObject(Object key) { return cache.remove(key); } @Override public void clear() { cache.clear(); } @Override public boolean equals(Object o) { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } if (this == o) { return true; } if (!(o instanceof Cache)) { return false; } Cache otherCache = (Cache) o; return getId().equals(otherCache.getId()); } @Override public int hashCode() { if (getId() == null) { throw new CacheException("Cache instances require an ID."); } return getId().hashCode(); } } /** * Lru (least recently used) cache decorator. * * @author Clinton Begin */ public class LruCache implements Cache { private final Cache delegate; private Map 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(size, .75F, true) { private static final long serialVersionUID = 4267176411845948333L; @Override protected boolean removeEldestEntry(Map.Entry eldest) { boolean tooBig = size() > size; if (tooBig) { 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) { keyMap.get(key); //touch return delegate.getObject(key); } @Override public Object removeObject(Object key) { return delegate.removeObject(key); } @Override public void clear() { delegate.clear(); keyMap.clear(); } private void cycleKeyList(Object key) { keyMap.put(key, key); if (eldestKey != null) { delegate.removeObject(eldestKey); eldestKey = null; } } }

参考资料

mybatis(六):设计模式 - 装饰器模式
Mybatis之设计模式之装饰者模式
mybatis 缓存的使用, 看这篇就够了

你可能感兴趣的:(设计模式)