Основное содержание этой статьи:
- Описание Шаблон Метод Шаблон
- Типичное применение шаблона метода анализа исходного кода
- Шаблон метода шаблона в сервлетах
- Шаблон метода шаблона в интерфейсе Mybatis BaseExecutor
Больше материалов можно найти в моем личном блоге:laijianfeng.org
Добро пожаловать, чтобы обратить внимание на общедоступную учетную запись [Xiao Xuanfeng] WeChat и получать сообщения в блоге вовремя
Шаблон метода шаблона
При разработке программ часто приходится сталкиваться с тем, что алгоритм, реализуемый методом, требует нескольких шагов, но некоторые из этих шагов фиксированы, а другие нет. Чтобы улучшить масштабируемость и удобство сопровождения кода, в этом сценарии пригодится шаблон метода шаблона.
Например, этапы создания онлайн-курса можно упростить до 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
Краткий код для этого выглядит следующим образом
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
Классная диаграмма класса выглядит следующим образом
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
Рекомендуемое чтение
Шаблоны проектирования | Простые фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны фабричных методов и типичные приложения
Шаблоны проектирования | Абстрактные фабричные шаблоны и типовые приложения
Шаблоны проектирования | Шаблоны построителей и типичные приложения
Шаблоны проектирования | Шаблоны прототипов и типовые приложения
Шаблоны проектирования | Шаблоны внешнего вида и типичные приложения
Режим дизайна | режим декоратора и типичное приложение
Шаблоны проектирования | Шаблоны адаптеров и типичные приложения
Шаблоны проектирования | Легковесные шаблоны и типичные приложения
Шаблоны проектирования | Комбинированные шаблоны и типичные приложения