Шаблонный метод (Template Method)
реализации: java, количество: 1
реализации(исходники)
+добавить
Шаблонный метод - паттерн поведения объектов, определяющий функциональность конктерных методов в рамках лишь абстрактных сущностей. источник оригинал codelib.ru codelib.ru
Условия, Задача, Назначение
Шаблонный метод определяет основу алгоритма в рамках абстрактных классов и методов, а подклассам позволяет переопределять отдельные шаги этого алгоритма (или все сразу), не изменяя, таким образом, его структуру и последовательность в целом.
Мотивация
Рассмотрим каркас приложения, в котором имеются классы Application и Document. Класс Application отвечает за открытие существующих документов, хранящихся во внешнем формате, например в виде файла. Объект класса Document представляет информацию документа после его прочтения из файла. codelib.ru codelib.ru источник оригиналНапример, графический редактор определит подклассы DrawApplication и DrawDocument, а электронная таблица — подклассы SpreadsheetApplication и SpreadsheetDocument. codelib.ru источник оригинал codelib.ru
оригинал источник codelib.ru codelib.ru
В абстрактном классе Application определен алгоритм открытия и считывания документа в операции OpenDocument:
void Application::OpenDocument (const char* name) { if (!CanOpenDocument(name)) { // работа с этим документом невозможна return; } Document* doc = DoCreateDocument(); if (doc) { _docs->AddDocument(doc); AboutToOpenDocument(doc); doc->0pen(); doc->DoRead(); } }
Application получить информацию о том, что документ вот-вот будет открыт (AboutToOpenDocument). Определяя некоторые шаги алгоритма с помощью абстрактных операций, шаблонный метод фиксирует их последовательность, но позволяет реализовать их в подклассах классов Application и Document. codelib.ru источник оригинал codelib.ru
Признаки применения, использования паттерна Шаблонный метод (Template Method)
Паттерн шаблонный метод следует использовать: codelib.ru codelib.ru оригинал источник- Чтобы однократно использовать инвариантные части алгоритма.
Оставляя реализацию изменяющегося поведения на усмотрение подклассов. источник codelib.ru оригинал codelib.ru - Когда нужно вычленить и локализовать в одном классе поведение.
Т.е. общую логику, общее поведение для всех подклассов, дабы избежать дублирования кода. Это хороший пример техники «вынесения за скобки с целью обобщения», описанной в работе Уильяма Опдайка (William Opdyke) и Ральфа Джонсона (Ralph Johnson). Сначала идентифицируются различия в существующем коде, а затем они выносятся в отдельные операции. В конечном итоге различающиеся фрагменты кода заменяются шаблонным методом, из которого вызываются новые операции. источник codelib.ru codelib.ru оригинал - Для управления расширениями подклассов.
Можно определить шаблонный метод так, что он будет вызывать операции-зацепки (hooks) - см. раздел «Результаты» - в определенных точках, разрешив тем самым расширение или изменение функциональности только в этих точках. оригинал источник codelib.ru codelib.ru
оригинал codelib.ru codelib.ru источник
- AbstractClass (Application) - абстрактный класс.
Определяет абстрактные примитивные операции, замещаемые в конкретных подклассах для реализации шагов алгоритма.
Реализует шаблонный метод, определяющий скелет алгоритма. Шаблонный метод вызывает примитивные операции, а также операции, определенные в классе AbstractClass или в других объектах. оригинал источник codelib.ru codelib.ru - ConcreteClass (MyApplication) - конкретный класс.
Реализует примитивные операции, выполняющие шаги алгоритма способом, который зависит от подкласса. codelib.ru оригинал источник codelib.ru
Схема использования паттерна Шаблонный метод (Template Method)
ConcreteClass предполагает, что инвариантные (зафиксированные) шаги алгоритма будут выполнены в AbstractClass. codelib.ru codelib.ru оригинал источник
Вопросы, касающиеся реализации паттерна Шаблонный метод (Template Method)
Аспекты, касающихся реализации: оригинал источник codelib.ru codelib.ru- Использование контроля доступа в C++.
В этом языке примитивные операции, которые вызывает шаблонный метод, можно объявить защищенными членами. Тогда гарантируется, что вызывать их сможет только сам шаблонный метод. Примитивные операции, которые обязательно нужно замещать, объявляются как чисто виртуальные функции. Сам шаблонный метод замещать не надо, так что его можно сделать невиртуальной функцией-членом. codelib.ru оригинал codelib.ru источник - Сокращение числа примитивных операций.
Важной целью при проектировании шаблонных методов является значимое сокращение числа примитивных операций, которые должны быть замещены в подклассах. Чем больше операций нужно замещать, тем утомительнее становится программирование клиента. codelib.ru источник оригинал codelib.ru - Соглашение об именах.
Выделить операции, которые необходимо заместить, можно путем добавления к их именам некоторого префикса. Например, в каркасе МасАрр для приложений на платформе Macintosh имена шаблонных методов начинаются с префикса Do: DoCreateDocument, DoRead и т.д. codelib.ru источник оригинал codelib.ru
Результаты
Шаблонные методы - один из фундаментальных приемов повторного использования кода. Они особенно важны в библиотеках классов, поскольку предоставляют возможность вынести общее поведение в библиотечные классы. источник codelib.ru codelib.ru оригинал- конкретные операции (либо из класса ConcreteClass, либо из классов клиента). оригинал источник codelib.ru codelib.ru
- конкретные операции из класса AbstractClass (то есть операции, полезные всем подклассам). codelib.ru оригинал codelib.ru источник
- примитивные операции (то есть абстрактные операции). оригинал codelib.ru источник codelib.ru
- фабричные методы (см. паттерн фабричный метод). codelib.ru codelib.ru оригинал источник
- операции-зацепки (hook operations), реализующие поведение по умолчанию, которое может быть расширено в подклассах. Часто такая операция по умолчанию не делает ничего. codelib.ru codelib.ru оригинал источник
void DerivedClass::Operation () { ParentClass::Operation(); // Расширенное поведение класса DerivedClass }
void ParentClass::Operation () { // Поведение родительского класса ParentClass HookOperation(); }
void ParentClass::HookOperation () { }
void DerivedClass::HookOperation () { // расширение в производном классе }
Пример
Паттерн шаблонный метод широко применяется при написании разного рода каркасов. codelib.ru источник codelib.ru оригинал
public void process(HttpServletRequest request, HttpServletResponse response) // Wrap multipart requests with a special wrapper request = processMultipart(request); // Identify the path component we will use to select a mapping if (path == null) { return; } if (log.isDebugEnabled()) { log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); } // Select a Locale for the current user if requested processLocale(request, response); // Set the content type and no-caching headers if requested processContent(request, response); processNoCache(request, response); // General purpose preprocessing hook if (!processPreprocess(request, response)) { return; } this.processCachedMessages(request, response); // Identify the mapping for this request ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; } // Check for any role required to perform this action if (!processRoles(request, response, mapping)) { return; } // Process any ActionForm bean related to this request ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); // Validate any fields of the ActionForm bean, if applicable try { if (!processValidate(request, response, form, mapping)) { return; } } catch (InvalidCancelException e) { ActionForward forward = processException(request, response, e, form, mapping); processForwardConfig(request, response, forward); return; throw e; } catch (ServletException e) { throw e; } // Process a forward or include specified by this mapping if (!processForward(request, response, mapping)) { return; } if (!processInclude(request, response, mapping)) { return; } // Create or acquire the Action instance to process this request if (action == null) { return; } // Call the Action instance itself ActionForward forward = processActionPerform(request, response, action, form, mapping); // Process the returned ActionForward instance processForwardConfig(request, response, forward); }
- processMultipart() – фабричный метод задействования специальной обработки multipart запросов: оригинал codelib.ru codelib.ru источник
- protected HttpServletRequest processMultipart(HttpServletRequest request) {
- if (!"POST".equalsIgnoreCase(request.getMethod())) {
- return (request);
- }
- if ((contentType != null)
- && contentType.startsWith("multipart/form-data")) {
- return (new MultipartRequestWrapper(request));
- } else {
- return (request);
- }
- }
- processPath() – фабричный метод определения запрошенного пути ресурса: codelib.ru оригинал источник codelib.ru
- HttpServletResponse response)
- String path;
- // For prefix matching, match on the path info (if any)
- if (path == null) {
- path = request.getPathInfo();
- }
- if ((path != null) && (path.length() > 0)) {
- return (path);
- }
- // For extension matching, strip the module prefix and extension
- if (path == null) {
- path = request.getServletPath();
- }
- if (!path.startsWith(prefix)) {
- log.error(msg + " " + request.getRequestURI());
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
- return null;
- }
- path = path.substring(prefix.length());
- int slash = path.lastIndexOf("/");
- int period = path.lastIndexOf(".");
- if ((period >= 0) && (period > slash)) {
- path = path.substring(0, period);
- }
- return (path);
- }
- processLocale() – фабричный метод получения локали обратившегося по http-пользователя: оригинал codelib.ru источник codelib.ru
- protected void processLocale(HttpServletRequest request,
- HttpServletResponse response) {
- // Are we configured to select the Locale automatically?
- if (!moduleConfig.getControllerConfig().getLocale()) {
- return;
- }
- // Has a Locale already been selected?
- HttpSession session = request.getSession();
- if (session.getAttribute(Globals.LOCALE_KEY) != null) {
- return;
- }
- // Use the Locale returned by the servlet container (if any)
- if (locale != null) {
- if (log.isDebugEnabled()) {
- log.debug(" Setting user locale '" + locale + "'");
- }
- session.setAttribute(Globals.LOCALE_KEY, locale);
- }
- }
- processContent() – фабричный метод выставления Content-Type-а ответа сервера: codelib.ru codelib.ru источник оригинал
- protected void processContent(HttpServletRequest request,
- HttpServletResponse response) {
- String contentType =
- moduleConfig.getControllerConfig().getContentType();
- if (contentType != null) {
- response.setContentType(contentType);
- }
- }
- processNoCache() – фабричный метод выставления заголовков запрета кеширования на стороне клиента (браузера): оригинал codelib.ru источник codelib.ru
- protected void processNoCache(HttpServletRequest request,
- HttpServletResponse response) {
- if (moduleConfig.getControllerConfig().getNocache()) {
- response.setHeader("Pragma", "No-cache");
- response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
- response.setDateHeader("Expires", 1);
- }
- }
- processPreprocess() – операция-зацепка для процедур общего назначения, потребность в которых может возникнуть именно в данном месте – после полной идентификации запроса и до запуска Action-а на выполнение: оригинал источник codelib.ru codelib.ru
- protected boolean processPreprocess(HttpServletRequest request,
- HttpServletResponse response) {
- return (true);
- }
и т.д. оригинал codelib.ru источник codelib.ru
Известные применения паттерна Шаблонный метод (Template Method)
Шаблонные методы настолько фундаментальны, что встречаются почти в каждом абстрактном классе. Очень широко шаблонные методы используются в различных фреймворках, например, из самых известных это: Struts, Spring. источник codelib.ru оригинал codelib.ruРодственные паттерны
Фабричные методы часто вызываются из шаблонных. В примере из раздела «Мотивация» шаблонный метод OpenDocument вызывал фабричный метод DoCreateDocument. codelib.ru источник codelib.ru оригиналРеализации: java(1) +добавить реализацию
1) RequestProcessor.java, code #528[автор:this]



