一、模板方法模式
在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序。但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关。
这时我们就需要定义一个模板结构,将具体内容延迟到子类去实现。
在不改变模板结构的前提下在子类中重新定义模板中的内容。
抽象类(AbstractClass): 定义抽象的原语操作(primitive operation) ,具体的子类将重定义它们以实现一个算法, 实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义
具体子类 (ConcreteClass): 实现原语操作以完成算法中与特定子类相关的步骤。
二、案例
我们举一个最简单的例子:
1. 开机--> 2.玩手机--> 3.关机
这是我们对手机的一个最基本的操作顺序,其中开机和关机是不变的,但是玩手机这个业务却是不同的,我们可能要社交,玩游戏,听音乐或者看电影。
实现
public abstract class AbstractClass_Phone{
/模板方法,用来控制操作手机的流程 (流程是一样的-复用)
/申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void phonePress(){
//第一步:开机
this.powerOn();
//第二步:玩手机
this.playPhone();
//第三步:关机
this.powerOff();
}
定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
/第一步:开机是一样的,所以直接实现
void powerOn(){
System.out.println("开机");
}
/第二步:玩手机是不一样的(社交,游戏)
//所以声明为抽象方法,具体由子类实现
abstract void playPhone();
/第三步:关机是一样的,所以直接实现
void powerOff(){
System.out.println("关机");
}
}
/社交的类
public class ConcreteClass_SheJiao extend AbstractClass_Phone{
@Override
public void playPhone(){
System.out.println(”聊QQ,微信“);
}
}
/游戏的类
public class ConcreteClass_Game extend AbstractClass_Phone{
@Override
public void playPhone(){
System.out.println(”打农药,吃鸡“);
}
}
public class TemplateMethod{
public static void main(String[] args){
/操作手机-->社交
ConcreteClass_SheJiao sheJiao= new ConcreteClass_SheJiao();
SheJiao.PhoneProcess();
/操作手机-->游戏
ConcreteClass_Game game= new ConcreteClass_Game();
Game.PhoneProcess();
}
结果输出:
开机
聊QQ,微信
关机
开机
打农药,吃鸡
关机
三、优缺点
优点:
- 提高代码复用性
- 将相同部分的代码放在抽象的父类中
- 提高了拓展性
- 将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为
- 实现了反向控制
通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制 & 符合“开闭原则”
缺点:
- 每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
四、应用场景
Servlet 中的模板方法模式
public abstract class HttpServlet extends GenericServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ... }
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
//...
} else if (method.equals(METHOD_HEAD)) {
//...
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//...
}
}
// ...省略...
}
其中service()方法是一个模板方法,这个方法调用了七个do方法中的一个或几个,完成对客户端的请求
在开发javaWeb应用时,自定义的Servlet类一般都扩展 HttpServlet 类
Mybatis的BaseExecutor接口中的模板方法模式
public abstract class BaseExecutor implements Executor {
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
protected int queryStack = 0;
private boolean closed;
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
// 省略....
}
BaseExecutor 中主要提供了缓存管理和事务管理的基本功能,继承 BaseExecutor 的子类只需要实现四个基本方法来完成数据库的相关操作即可,这四个方法分别是:doUpdate() 方法、doQuery() 方法、doQueryCursor() 方法、doFlushStatement() 方法,其余功能都在 BaseExecutor 中实现。