Шаблоны проектирования | Шаблоны методов шаблонов и типичные приложения

задняя часть алгоритм Шаблоны проектирования MyBatis

Основное содержание этой статьи:

  • Описание Шаблон Метод Шаблон
  • Типичное применение шаблона метода анализа исходного кода
    • Шаблон метода шаблона в сервлетах
    • Шаблон метода шаблона в интерфейсе Mybatis BaseExecutor

Больше материалов можно найти в моем личном блоге:laijianfeng.org
Добро пожаловать, чтобы обратить внимание на общедоступную учетную запись [Xiao Xuanfeng] WeChat и получать сообщения в блоге вовремя

关注【小旋锋】微信公众号


Шаблон метода шаблона

При разработке программ часто приходится сталкиваться с тем, что алгоритм, реализуемый методом, требует нескольких шагов, но некоторые из этих шагов фиксированы, а другие нет. Чтобы улучшить масштабируемость и удобство сопровождения кода, в этом сценарии пригодится шаблон метода шаблона.

Например, этапы создания онлайн-курса можно упростить до 4 шагов:

  1. Сделать РРТ
  2. запись видео
  3. писать заметки
  4. Предоставить материалы курса

Действия 1, 2 и 3 фиксированы во всех курсах, шаг 3 является необязательным, а шаг 4 отличается в каждом курсе (некоторые курсы должны предоставить исходный код, некоторые — файлы изображений и т. д.)

Мы можем определить шаг из родительского класса и реализовать фиксированные шаги, а нефиксированный шаг оставить для реализации подкласса. Вы даже можете передать метод ловушки, чтобы создать подклассы, чтобы определить, является ли выполнение метода в процессе

示例.模板方法模式

Шаблон метода шаблона: определяет структуру алгоритма в операции, при этом некоторые шаги откладываются до подклассов. Паттерн Template Method позволяет подклассам переопределять определенные шаги алгоритма без изменения его структуры. Шаблон метода шаблона — это технология повторного использования кода, основанная на наследовании, которое представляет собой шаблон поведения класса.

Роль

AbstractClass (абстрактный класс): В абстрактном классе определен ряд базовых операций (PrimitiveOperations).Эти базовые операции могут быть конкретными или абстрактными.Каждая базовая операция соответствует шагу алгоритма, который может быть переопределен или реализован в его подклассах.step. При этом в абстрактном классе реализован шаблонный метод (Template Method) для определения основы алгоритма, который может не только вызывать базовые методы, реализованные в абстрактном классе, но и вызывать подклассы, реализованные в абстрактном классе. абстрактный класс.Основные методы, а также вы можете вызывать методы в других объектах.

ConcreteClass (подкласс бетона): Это дочерний элемент абстрактного класса, для выполнения операций, объявленных в абстрактном базовом классе, для завершения определенного алгоритма шага родительских подклассов, которые могут быть покрыты конкретными базовыми операциями в родительском классе.

Шаблонный метод — это метод, определенный в абстрактном классе, который группирует основные методы операций для формирования общего алгоритма или общего поведения. Этот шаблонный метод определен в абстрактном классе и полностью наследуется подклассами без изменений. Шаблонный метод — это конкретный метод, который обеспечивает логическую структуру верхнего уровня, а этапы логической композиции в абстрактном классе могут быть конкретными или абстрактными методами.

Базовый метод — это метод, реализующий каждый шаг алгоритма и являющийся частью шаблонного метода. Существует три основных метода: абстрактный метод, конкретный метод и метод крючка.

  • Абстрактный метод: абстрактный метод объявляется абстрактным классом и реализуется его конкретными подклассами.
  • Конкретный метод: Конкретный метод объявляется и реализуется абстрактным классом или конкретным классом, а его подклассы могут быть переопределены или напрямую унаследованы.
  • Методы-ловушки: могут быть «зацеплены» некоторыми конкретными шагами для выполнения разных шагов в методе шаблона в разных условиях.

Пример

Поэтому мы шаблонный метод в абстрактном классе зафиксировали весь процесс, который завершен 1,2,3, реализованный в абстрактном классе, причем 3 выполняются подклассом или управляются методом ловушки, 4 подклассом для реализации

Абстрактный класс определяется следующим образом:

public abstract class ACourse {

    protected final void makeCourse() {
        this.makePPT();
        this.makeVideo();
        if (needWriteArticle()) {
            this.writeArticle();
        }
        this.packageCourse();
    }

    final void makePPT() {
        System.out.println("1. 制作PPT");
    }

    final void makeVideo() {
        System.out.println("2. 制作视频");
    }

    final void writeArticle() {
        System.out.println("3. 编写课程笔记");
    }

    //钩子方法
    protected boolean needWriteArticle() {
        return false;
    }

    abstract void packageCourse();
}

один из нихmakeCourseМетод представляет собой шаблонный метод, который определяет основной процесс создания онлайн-курса.makePPT,makeVideo,writeArticleЭти три шага зафиксированы во всех курсах, поэтому используйтеfinalмодификация ключевых слов;packageCourseМетод может быть разным во всех классах, поэтому он объявляется как абстрактный метод и реализуется подклассом.needWriteArticleвернутьbooleanЗначение типа, которое определяет, пишутся ли заметки курса или нет.

Подкласс JavaCourse, реализующий абстрактный методpackageCourse, переопределяя метод ловушкиneedWriteArticle

public class JavaCourse extends ACourse {
    @Override
    void packageCourse() {
        System.out.println("4. 提供Java课程源代码");
    }

    @Override
    protected boolean needWriteArticle() {
        return true;
    }
}

Подкласс FECourse, реализующий абстрактный методpackageCourse, переопределяя метод ловушкиneedWriteArticle, в котором результат метода ловушки передается клиенту для определения

public class FECourse extends ACourse {
    private boolean needWriteArticleFlag = false;
    @Override
    void packageCourse() {
        System.out.println("4.1 提供课程的前端代码");
        System.out.println("4.2 提供课程的图片等多媒体素材");
    }

    public FECourse(boolean needWriteArticleFlag) {
        this.needWriteArticleFlag = needWriteArticleFlag;
    }

    @Override
    protected boolean needWriteArticle() {
        return this.needWriteArticleFlag;
    }
}

клиентский тест

public class Test {
    public static void main(String[] args) {
        System.out.println("Java课程start---");
        ACourse javaCourse = new JavaCourse();
        javaCourse.makeCourse();
        System.out.println("Java课程end---\n");


        System.out.println("前端课程start---");
        ACourse feCourse = new FECourse(false);
        feCourse.makeCourse();
        System.out.println("前端课程end---");
    }
}

выходной результат

Java课程start---
1. 制作PPT
2. 制作视频
3. 编写笔记
4. 提供Java课程源代码
Java课程end---

前端课程start---
1. 制作PPT
2. 制作视频
4.1 提供课程的前端代码
4.2 提供课程的图片等多媒体素材
前端课程end---

Их диаграммы классов следующие

示例.模板方法模式

Резюме шаблона метода шаблона

шаблон шаблона методаглавное преимуществоследующим образом:

  • Алгоритм формально определен в родительском классе, а его подклассы реализуют детальную обработку.Когда подклассы реализуют алгоритм детальной обработки, порядок выполнения шагов в алгоритме не изменяется.
  • Шаблон метода шаблона — это технология повторного использования кода, которая особенно важна при проектировании библиотек классов.Он извлекает общее поведение из библиотеки классов, помещает общее поведение в родительский класс и реализует другое поведение через его подклассы.Это поощряет нам правильно использовать наследование для повторного использования кода.
  • Может быть реализована обратная структура управления, когда подкласс переопределяет метод ловушки суперкласса, чтобы определить, нужно ли выполнять конкретный шаг.
  • В режиме шаблонного метода базовые методы родительского класса могут быть переопределены подклассами.Разные подклассы могут предоставлять разные реализации базовых методов.Очень удобно заменять и добавлять новые подклассы, что соответствует принципу единой ответственности и принцип открыто-закрыто.

шаблон шаблона методаглавный недостатокследующим образом:

  • Необходимо предусмотреть подкласс для разных реализаций каждого базового метода.Если в родительском классе будет слишком много переменных базовых методов, то количество классов увеличится, система станет больше, а дизайн будет более абстрактным. В этом случае можно комбинировать перемычки шаблона с дизайном.

Применимая сцена:

  • Разделите некоторые сложные алгоритмы, спроектируйте фиксированные части алгоритма как шаблонные методы и конкретные методы родительского класса, а некоторые детали, которые можно изменить, реализуйте его подклассами. То есть: реализовать инвариантные части алгоритма один раз и оставить изменяемое поведение подклассам.
  • Поведение, общее для подклассов, должно быть выделено и сосредоточено в общем родительском классе, чтобы избежать дублирования кода.
  • Необходимо определить, выполняется ли шаг в алгоритме родительского класса через подкласс, чтобы реализовать обратное управление подклассом родительскому классу.

Типичное применение шаблона метода анализа исходного кода

Шаблон метода шаблона в сервлетах

Servlet(Серверный апплет) — это аббревиатура от Java Servlet, серверной программы, написанной на Java, основной функцией которой является интерактивный просмотр и изменение данных, а также создание динамического веб-контента. в каждомServletдолжны быть реализованыServletинтерфейс,GenericServletэто общий, неспецифический сервлет протокола, который реализуетServletинтерфейс, при этомHttpServletунаследовано отGenericServlet, выполненоServletинтерфейс, дляServletИнтерфейс предоставляет общую реализацию для работы с протоколом HTTP, поэтому мы определяемServletпросто наследуйHttpServletВот и все.

HttpServlet的继承关系

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)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } 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 {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    
    // ...省略...
}

существуетHttpServletизserviceВ методе сначала получите запрошенное имя метода, а затем вызовите соответствующий метод в соответствии с именем метода.doXXXметод, например, метод запроса — GET, затем вызовитеdoGetметод; метод запроса — POST, затем вызовитеdoPostметод

HttpServletЭто эквивалентно определению набора шаблонов для обработки HTTP-запросов;serviceМетод является шаблонным методом, определяющим базовый процесс обработки HTTP-запросов;doXXXа другие методы являются базовыми методами, и соответствующая обработка выполняется по методу запроса, и подклассы могут переопределять эти методы;HttpServletRequestМетод в нем действует как метод ловушки.

При разработке приложений javaWeb пользовательские классы сервлетов обычно расширяются.HttpServletкласс, например, мы реализуем выводHello World!изServletследующим образом

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

// 扩展 HttpServlet 类
public class HelloWorld extends HttpServlet {

  public void init() throws ServletException {
    // ...
  }

  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<h1>Hello World!</h1>");
  }
  
  public void destroy() {
      // ...
  }
}

обычайServletпереписанныйdoGetметод, когда клиент инициирует запрос GET, он получит<h1>Hello World!</h1>.

Шаблон метода шаблона в интерфейсе Mybatis BaseExecutor

ExecutorЭто один из основных интерфейсов Mybatis, который определяет основные методы операций с базой данных.Код этого интерфейса выглядит следующим образом:

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;
  // 执行 update、insert、delete 三种类型的SQL语句
  int update(MappedStatement ms, Object parameter) throws SQLException;
  // 执行selete类型的SQL语句,返回值分为结果对象列表或游标对象
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  /
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; 
  // 批量执行SQL语句
  List<BatchResult> flushStatements() throws SQLException;
  // 提交事务
  void commit(boolean required) throws SQLException;
  // 回滚事务
  void rollback(boolean required) throws SQLException;
  // 创建缓存中用到的CacheKey对象
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  // 根据CacheKey对象查找缓存
  boolean isCached(MappedStatement ms, CacheKey key);
  // 清空一级缓存
  void clearLocalCache();
  // 延迟加载一级缓存中的数据
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  // 获取事务对象
  Transaction getTransaction();
  // 关闭Executor对象
  void close(boolean forceRollback);
  // 检测Executor是否已关闭
  boolean isClosed();

  void setExecutorWrapper(Executor executor);

}

ExecutorКлассная диаграмма класса выглядит следующим образом

Executor与其子类的类图

BaseExecutorВ основном он обеспечивает основные функции управления кешем и транзакциями, наследованиеBaseExecutorПодклассы должны реализовать только четыре основных метода для выполнения операций, связанных с базой данных.Четыре метода:doUpdate()метод,doQuery()метод,doQueryCursor()метод,doFlushStatement()метод, остальные функции находятся вBaseExecutorреализовано в.

BaseExecutorЧасть кода выглядит следующим образом, гдеquery()метод сначала создастCacheKeyобъект, и согласноCacheKeyОбъект выполняет поиск в кеше первого уровня. Если кеш попадает, он возвращает объект результата, записанный в кеше. Если он не попадает, выполняется запрос к базе данных, чтобы получить набор результатов, а затем набор результатов сопоставляется с результатом. объект и сохраняется в кэше первого уровня, и в то же время возвращается объект результата.

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Существует четыре подклассаSimpleExecotor,ReuseExecutor,BatchExecutor,ClosedExecutor, так как здесь используется шаблон метода шаблона, фиксированные операции, такие как кеш первого уровня, инкапсулированы вBaseExecutorСледовательно, подклассам не нужно заботиться о таких операциях, как кэш первого уровня, а нужно сосредоточиться только на реализации четырех основных методов.

Вот краткое введение в функции этих четырех подклассов:

  • SimpleExecutorОн используется по умолчанию, когда Mybatis выполняет оператор Mapper.Executor, предоставляет самую базовую функцию выполнения оператора Mapper без слишком большой инкапсуляции.
  • ReuseExecutorпри условииStatementповторно используемые функции черезstatementMapИспользуется кеш полейStatementПовторное использование объектов может уменьшить предварительную компиляцию SQL, а также создание и уничтожение.StatementНакладные расходы на объект, что повышает производительность
  • BatchExecutorОн реализует функцию пакетирования нескольких операторов SQL, кэширует несколько SQL-запросов на стороне клиента и упаковывает несколько SQL-запросов в базу данных для выполнения в нужное время, тем самым снижая нагрузку на сеть и повышая производительность системы.
  • ClosedExecutorпросто внутренний класс класса

Ссылаться на:
Лю Вэй: Шаблоны проектирования Java Edition
MOOC Java Design Patterns Интенсивный метод отладки + анализ памяти
Сюй Цзюньмин: Исполнитель Mybatis Technology Insider 3.6

Рекомендуемое чтение

Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Режим дизайна | режим декоратора и типичное приложение
Шаблоны проектирования | Шаблоны адаптеров и типичные приложения
Шаблоны проектирования | Легковесные шаблоны и типичные приложения
Шаблоны проектирования | Комбинированные шаблоны и типичные приложения