学习开源框架源码,除了储备点知识以便于与面试官互相忽略之外,我想最重要的还是去学习大神如何写代码,如何做到职责单一,如何做到可扩展等。。。
本篇,试着总结一下mybatis在缓存模块使用到的装饰模式。
或许一说到装饰模式就会扯到装饰模式四种角色,但我觉得这些都是扯蛋,没必需照本宣科,我觉得myabtis框架也不是完全的装饰模式,或许可以说是变异版本。
其实,我觉得大多的设计模式无非就是面向接口编程与接口引用的组合罢了!
下面看看mybatis框架的cache模块是如何使用装饰模式的
1. 首先,定义了一个cache接口,具体的功能都在接口中说明了,主要是putObject 和getObject
public interface Cache { String getId(); void putObject(Object key, Object value); Object getObject(Object key); Object removeObject(Object key); void clear(); int getSize(); default ReadWriteLock getReadWriteLock() { return null; } }
2. 然后,再看一下Cache的继承体系
清一色的java类,并没有啥抽象类或者接口,这就与教科书中的装饰模式有点区别了。。。。
3. 每一个Cache实现类都持有一个Cache引用
看下LoggingCache类的源代码
public class LoggingCache implements Cache { private final Log log; private final Cache delegate; protected int requests = 0; protected int hits = 0; public LoggingCache(Cache delegate) { this.delegate = delegate; this.log = LogFactory.getLog(getId()); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { return delegate.getSize(); } @Override public void putObject(Object key, Object object) { delegate.putObject(key, object); } @Override public Object getObject(Object key) { requests++; final Object value = delegate.getObject(key); if (value != null) { hits++; } if (log.isDebugEnabled()) { log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio()); } return value; } @Override public Object removeObject(Object key) { return delegate.removeObject(key); } @Override public void clear() { delegate.clear(); } @Override public int hashCode() { return delegate.hashCode(); } @Override public boolean equals(Object obj) { return delegate.equals(obj); } private double getHitRatio() { return (double) hits / (double) requests; } }
LoggingCache类的构造器传入了一个Cache实例delegate, 再看getObject方法
这么一看,貌似很符合装饰模式的精神内核,但是并没有照搬装饰模式的套路。而且,,,这似乎与责任链的套路也很像。。。。
假设LoggingCache的构造器入参是FifoCache, 而FifoCache的构造器入参又是RedsiCache, 那么在调用LoggingCache#getObject方法时就会形成一条链:
LoggingCache#getObject()-----------> FifoCache#getObject()--------------------> RedisCache#getObject() (这难道不能算责任链嘛,当然与真正的责任又有些差别),
而且这条调用链中,LoggingCache#getObject ()进行前、后功能增强,而FifoCache#getObject()-进行了前置功能增强, 当然RedisCache#getObject()仅是查询redis库。
mybatis框架就是这样操作的,这条调用链具体怎么组合,完全看Cache的Caller是如何去构造链节点的关系,使用得相当灵活,我觉得它相当于: 责任链+装饰。
而实现这种套路的技术就是Cache的所有实现类中都持有了一个Cache对象的引用。 注意是Cache对象,而非Cache的具体实现对象!
或许这就是面向接口编程吧!
在以后工作中,能用就尽可能用吧,切记!