什么是装饰模式(Decorator):
装饰模式是为已有功能动态的添加更多功能的一种方式;在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象;
装饰模式由4种角色组成:
抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加职责的对象;
具体构件(Concrete Component)角色:定义一个将要接收附加职责的类;
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口,从外类来扩展Component类的功能,但对于Component类来说,是无需知道Decorato的存在的;
具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的职责;
装饰模式的优点:
1.把类中的装饰功能从类中搬移出去,这样可以简化原有的类;
2.有效的把类的核心职责和装饰功能分开,而且可以去除相关类中重复的装饰逻辑;
3.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性;
4.通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合;
装饰模式的应用场景,以下引自百度百科:
1. 需要扩展一个类的功能,或给一个类添加附加职责;
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销;
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实;
4. 当不能采用生成子类的方法进行扩充时。
一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类;
模式简化
如果只有一个Concrete Component类而没有抽象的Component接口时,可以让Decorator继承Concrete Component;
如果只有一个Concrete Decorator类时,可以将Decorator和Concrete Decorator合并;
UML类图
根据UML生成一个简单的Demo:
interface Component{ void operation(); } class ConcreateComponent implements Component{ @Override public void operation() { System.out.println("balabala...."); } } abstract class Decorator implements Component{ private Component comp; public Decorator(Component comp){ this.comp = comp; } @Override public void operation() { comp.operation(); } } class ConcreateDecorator extends Decorator{ public ConcreateDecorator(Component comp) { super(comp); } @Override public void operation() { System.out.println("begin..."); super.operation(); System.out.println("end..."); } } public class TestMain { public static void main(String[] args) { Component component = new ConcreateComponent(); component = new ConcreateDecorator(component); component.operation(); } }
输出的结果为:
begin...
balabala....
end...
在Mybatis的Executor也采用了装饰模式,不过在使用方法上稍微有些区别,我们先看一下Executor的实现图:
以下是我从Mybatis中分离出来的一部分代码,标红部分可以重点关注一下:
package org.apache.ibatis.executor;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
Transaction getTransaction();
void setExecutorWrapper(Executor var1);
}
package org.apache.ibatis.executor;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.jdbc.ConnectionLogger;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public abstract class BaseExecutor implements Executor {
protected Transaction transaction;
protected Executor wrapper;
protected Configuration configuration;
protected int queryStack;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.configuration = configuration;
this.wrapper = this;
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
return null;
}
@Override
public void commit(boolean required) throws SQLException {
}
@Override
public void rollback(boolean required) throws SQLException {
}
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return null;
}
@Override
public Transaction getTransaction() {
return null;
}
@Override
public void setExecutorWrapper(Executor var1) {
this.wrapper = wrapper;
}
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
}
package org.apache.ibatis.executor; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.logging.Log; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.transaction.Transaction; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class SimpleExecutor extends BaseExecutor { protected SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; } }
package org.apache.ibatis.executor; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.cache.TransactionalCacheManager; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; import java.sql.SQLException; import java.util.List; public class CachingExecutor implements Executor{ private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException { return null; } @Override public void commit(boolean required) throws SQLException { } @Override public void rollback(boolean required) throws SQLException { } @Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { return null; } @Override public Transaction getTransaction() { return null; } @Override public void setExecutorWrapper(Executor var1) { throw new UnsupportedOperationException("This method should not be called"); } }
package org.apache.ibatis.session;
import org.apache.ibatis.executor.*;
import org.apache.ibatis.plugin.InterceptorChain;
import org.apache.ibatis.transaction.Transaction;
public class Configuration {
private Configuration conf;
public Configuration(Configuration conf){
this.conf = conf;
}
protected boolean cacheEnabled = true;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected final InterceptorChain interceptorChain = new InterceptorChain();
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(conf, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}