一、模板方法模式

在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序。但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关。

这时我们就需要定义一个模板结构,将具体内容延迟到子类去实现。

在不改变模板结构的前提下在子类中重新定义模板中的内容。

aHR0cHM6Ly9pbWctbXkuY3Nkbi5uZXQvdXBsb2Fkcy8yMDEyMDUvMTQvMTMzNjk2NTA5M18xMDQ4LmpwZw.jpg

抽象类(AbstractClass): 定义抽象的原语操作(primitive operation) ,具体的子类将重定义它们以实现一个算法, 实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义
具体子类 (ConcreteClass):  实现原语操作以完成算法中与特定子类相关的步骤。

二、案例

我们举一个最简单的例子:

              1. 开机--> 2.玩手机--> 3.关机

这是我们对手机的一个最基本的操作顺序,其中开机和关机是不变的,但是玩手机这个业务却是不同的,我们可能要社交,玩游戏,听音乐或者看电影。

实现

步骤1: 创建抽象模板结构(Abstract Class):操作手机的步骤

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("关机");  
    }   
}

步骤2: 创建具体模板(Concrete Class),即”社交“和”游戏“的具体步骤

 /社交的类
  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(”打农药,吃鸡“);  
    }   
}

步骤3: 客户端调用-玩手机

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 中的模板方法模式

HttpServlet 的简要代码如下所示

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接口中的模板方法模式

Executor 是 Mybatis 的核心接口之一,其中定义了数据库操作的基本方法,该接口的代码如下:

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 中实现。


版权声明:文章转载请注明来源,如有侵权请联系博主删除!
最后修改:2019 年 12 月 25 日 01 : 48 PM
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!