设计模式-问题-委派模式+模板模式+策略模式+责任链模式

委派模式

委派主要解决什么问题,什么情况下要委派。

主要是负责任务的调度和处理,我们有一个管理者,和一些执行者,管理者把任务分配给执行者的情况下需要委派。

也可以说是一个大任务需要拆分成多个子任务,但是别人只需要我们把任务执行完,这时候就可以采用委派模式。

模板模式

模板方法本质解决了一个什么问题,回忆一下spring/mybatis怎么用模板模式的?你在工作中会用了嘛?

解决问题

  • 子类有共同的方法

    • 通过父类来拼接模板

  • 封装不变的部分,扩展可变的部分

  • 行为由父类控制,子类负责实现

Spring模板方法

  • JDBCTemplate

  • FrameworkServlet

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    
      long startTime = System.currentTimeMillis();
      Throwable failureCause = null;
    
      LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
      LocaleContext localeContext = buildLocaleContext(request);
    
      RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
      ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    
      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    
      initContextHolders(request, localeContext, requestAttributes);
    
      try {
        doService(request, response);
      }
      catch (ServletException ex) {
        failureCause = ex;
        throw ex;
      }
      protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
        throws Exception;
  • AbstractBeanFactory

  • AbstractRoutingDataSource

  • org.springframework.web.servlet.view.AbstractTemplateView

Mybatis模板方法

Mybatis BaseExecute

image-20210107170130094

Executor是一个基础的SQL执行类,他实现了大部分的SQL执行逻辑,然后把几个方法交给子类定制化来完成。

例如

  • doUpdate

  • doFlushStatement

  • doQuery

  • doQueryCursor

查询方法

看到org.apache.ibatis.executor.BaseExecutor#commit他的提交方法会调用刷新也就是提交到数据库去,但是具体的实现可能提交的时机是不一样的,例如SimpleExecutor是立即提交,但是BatchExecutor是存一堆然后一起提交。

public void commit(boolean required) throws SQLException {
  if (closed) {
    throw new ExecutorException("Cannot commit, transaction is already closed");
  }
  clearLocalCache();
  flushStatements();
  if (required) {
    transaction.commit();
  }
}

public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  return doFlushStatements(isRollBack);
}

protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
  throws SQLException;

此时查看具体的类的doFlushStatements

ReuseExecutor

org.apache.ibatis.executor.ReuseExecutor#doFlushStatements

  private final Map<String, Statement> statementMap = new HashMap<>();

public List<BatchResult> doFlushStatements(boolean isRollback) {
  for (Statement stmt : statementMap.values()) {
    closeStatement(stmt);
  }
  statementMap.clear();
  return Collections.emptyList();
}

ReuseExecutor因为需要重用相同语句的Statement所以在提交的时候需要把那些都清空

SimpleExecutor

org.apache.ibatis.executor.SimpleExecutor#doQuery

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    return Collections.emptyList();
  }

SimpleExecutor则不需要做任何操作

BatchExecutor

org.apache.ibatis.executor.BatchExecutor#doQuery

public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<>();
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        BatchResult batchResult = batchResultList.get(i);
        try {
          batchResult.setUpdateCounts(stmt.executeBatch());
          MappedStatement ms = batchResult.getMappedStatement();
          List<Object> parameterObjects = batchResult.getParameterObjects();
          KeyGenerator keyGenerator = ms.getKeyGenerator();
          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
          } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
            for (Object parameter : parameterObjects) {
              keyGenerator.processAfter(this, ms, stmt, parameter);
            }
          }
          // Close statement to close cursor #1109
          closeStatement(stmt);
        } catch (BatchUpdateException e) {
          StringBuilder message = new StringBuilder();
          message.append(batchResult.getMappedStatement().getId())
              .append(" (batch index #")
              .append(i + 1)
              .append(")")
              .append(" failed.");
          if (i > 0) {
            message.append(" ")
                .append(i)
                .append(" prior sub executor(s) completed successfully, but will be rolled back.");
          }
          throw new BatchExecutorException(message.toString(), e, results, batchResult);
        }
        results.add(batchResult);
      }
      return results;
    } finally {
      for (Statement stmt : statementList) {
        closeStatement(stmt);
      }
      currentSql = null;
      statementList.clear();
      batchResultList.clear();
    }
  }

BatchExecutor则需要一批一批的来提交

所以可以看到公共的方法在父类中实现了,具体的差异性的内容都交给子类来实现钩子方法了

策略模式

为什么还需要封装一个工厂来使用策略模式

  • 通过工厂来获取具体的策略这样我们就能够多次去选择和使用策略

    • 我们通过工厂来记录当前有哪一些策略模式

    • 通过工厂我们每次创建新的策略不需要来改动过多的代码,并且能够让我们对扩展开发对修改关闭

  • 策略模式有弊端,客户端必须知道所有策略,并且需要自己来决定使用哪一个策略

策略模式使用前提是什么?即满足什么条件你才能去选策略?真正用的时候需要告诉客户端什么

  • 使用前提

    • 不同的类行为是不一样的

    • 客户端使用只会使用到其中一种策略

    • 需要屏蔽不同算法和算法之间的关系

  • 客户端需要知道

    • 需要知道所有策略,并且需要自行选择其中一个

责任链模式

想一想责任链模式的使用场景请具体举出来,请认真体会一下责任链模式的优点

  • 能够帮我们把请求和处理分开,使用者不需要知道是谁处理的

    • 需要注意链路过长的问题,一般通过增加链路的时候判断最大长度来避免无意识破坏系统性鞥

  • 常用场景

    • 例如公司里面发起审批流程

      • 需要先部门经理审核然后是技术总监审核最后是老板

    • 例如我们对请求的拦截过滤FilterChain也是一层一层处理的

    • 比如说Netty中的pipeline

  • 只要是处理着不需要知道请求的全貌,服务端可以把处理逻辑构建成一条链路的都能够使用责任链来使用

Last updated