CodeLAB
на главную карта сайта обратная связь
каталог | задачи | паттерны | исходники | стат | форумы | ссылки
 гость
искать в
Главная >> Каталог задач >> Паттерны >> Поведения >> Шаблонный метод (Template Method)

<< назад
распечатать обсудить >>


Шаблонный метод (Template Method)
реализации: java, количество: 1

Aвтор: this
Дата: 08.10.2007
Просмотров: 59778
Рейтинг: 0/0,0(0)
+
реализации(исходники) +добавить

Имя

«Паттерн
Template Method» codelab.ru codelab.ru источник оригинал

Шаблонный метод - паттерн поведения объектов, определяющий функциональность конктерных методов в рамках лишь абстрактных сущностей. codelab.ru оригинал источник codelab.ru

Условия, Задача, Назначение

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

Мотивация

Рассмотрим каркас приложения, в котором имеются классы Application и Document. Класс Application отвечает за открытие существующих документов, хранящихся во внешнем формате, например в виде файла. Объект класса Document представляет информацию документа после его прочтения из файла. codelab.ru оригинал codelab.ru источник
Приложения, построенные на базе этого каркаса, могут порождать подклассы от классов Application и Document, отвечающие конкретным потребностям. оригинал codelab.ru codelab.ru источник

Например, графический редактор определит подклассы DrawApplication и DrawDocument, а электронная таблица — подклассы SpreadsheetApplication и SpreadsheetDocument. codelab.ru источник оригинал codelab.ru

источник оригинал codelab.ru codelab.ru

В абстрактном классе Application определен алгоритм открытия и считывания документа в операции OpenDocument:

 открытие, считывание документа [C++]  ссылка
  1. void Application::OpenDocument (const char* name) {
  2. if (!CanOpenDocument(name)) {
  3. // работа с этим документом невозможна
  4. return;
  5. }
  6.  
  7. Document* doc = DoCreateDocument();
  8.  
  9. if (doc) {
  10. _docs->AddDocument(doc);
  11. AboutToOpenDocument(doc);
  12. doc->0pen();
  13. doc->DoRead();
  14. }
  15. }
codelab.ru codelab.ru источник оригинал

Операция OpenDocument определяет все шаги открытия документа. Она проверяет, можно ли открыть документ, создает объект класса Document, добавляет его к набору документов и считывает документ из файла. источник codelab.ru codelab.ru оригинал
Операцию вида OpenDocument мы будем называть шаблонным методом, описывающим алгоритм в терминах абстрактных операций, которые замещены в подклассах для получения нужного поведения. Подклассы класса Application выполняют проверку возможности открытия (CanOpenDocument) и создания документа (DoCreateDocument). Подклассы класса Document считывают документ (DoRead). codelab.ru источник оригинал codelab.ru
Шаблонный метод определяет также операцию, которая позволяет подклассам codelab.ru codelab.ru источник оригинал

Application получить информацию о том, что документ вот-вот будет открыт (AboutToOpenDocument). Определяя некоторые шаги алгоритма с помощью абстрактных операций, шаблонный метод фиксирует их последовательность, но позволяет реализовать их в подклассах классов Application и Document. оригинал codelab.ru codelab.ru источник

Признаки применения, использования паттерна Шаблонный метод (Template Method)

Паттерн шаблонный метод следует использовать: оригинал codelab.ru codelab.ru источник
  1. Чтобы однократно использовать инвариантные части алгоритма.
    Оставляя реализацию изменяющегося поведения на усмотрение подклассов. источник codelab.ru оригинал codelab.ru
  2. Когда нужно вычленить и локализовать в одном классе поведение.
    Т.е. общую логику, общее поведение для всех подклассов, дабы избежать дублирования кода. Это хороший пример техники «вынесения за скобки с целью обобщения», описанной в работе Уильяма Опдайка (William Opdyke) и Ральфа Джонсона (Ralph Johnson). Сначала идентифицируются различия в существующем коде, а затем они выносятся в отдельные операции. В конечном итоге различающиеся фрагменты кода заменяются шаблонным методом, из которого вызываются новые операции. codelab.ru codelab.ru оригинал источник
  3. Для управления расширениями подклассов.
    Можно определить шаблонный метод так, что он будет вызывать операции-зацепки (hooks) - см. раздел «Результаты» - в определенных точках, разрешив тем самым расширение или изменение функциональности только в этих точках. codelab.ru codelab.ru оригинал источник

Решение

оригинал codelab.ru источник codelab.ru

оригинал codelab.ru источник codelab.ru

Участники паттерна Шаблонный метод (Template Method)

codelab.ru оригинал codelab.ru источник
  1. AbstractClass (Application) - абстрактный класс.
    Определяет абстрактные примитивные операции, замещаемые в конкретных подклассах для реализации шагов алгоритма.
    Реализует шаблонный метод, определяющий скелет алгоритма. Шаблонный метод вызывает примитивные операции, а также операции, определенные в классе AbstractClass или в других объектах. оригинал codelab.ru codelab.ru источник
  2. ConcreteClass (MyApplication) - конкретный класс.
    Реализует примитивные операции, выполняющие шаги алгоритма способом, который зависит от подкласса. codelab.ru codelab.ru оригинал источник

Схема использования паттерна Шаблонный метод (Template Method)

ConcreteClass предполагает, что инвариантные (зафиксированные) шаги алгоритма будут выполнены в AbstractClass. codelab.ru codelab.ru оригинал источник

Вопросы, касающиеся реализации паттерна Шаблонный метод (Template Method)

Аспекты, касающихся реализации: codelab.ru codelab.ru источник оригинал
  1. Использование контроля доступа в C++.
    В этом языке примитивные операции, которые вызывает шаблонный метод, можно объявить защищенными членами. Тогда гарантируется, что вызывать их сможет только сам шаблонный метод. Примитивные операции, которые обязательно нужно замещать, объявляются как чисто виртуальные функции. Сам шаблонный метод замещать не надо, так что его можно сделать невиртуальной функцией-членом. оригинал источник codelab.ru codelab.ru
  2. Сокращение числа примитивных операций.
    Важной целью при проектировании шаблонных методов является значимое сокращение числа примитивных операций, которые должны быть замещены в подклассах. Чем больше операций нужно замещать, тем утомительнее становится программирование клиента. источник оригинал codelab.ru codelab.ru
  3. Соглашение об именах.
    Выделить операции, которые необходимо заместить, можно путем добавления к их именам некоторого префикса. Например, в каркасе МасАрр для приложений на платформе Macintosh имена шаблонных методов начинаются с префикса Do: DoCreateDocument, DoRead и т.д. оригинал codelab.ru codelab.ru источник

Результаты

Шаблонные методы - один из фундаментальных приемов повторного использования кода. Они особенно важны в библиотеках классов, поскольку предоставляют возможность вынести общее поведение в библиотечные классы. codelab.ru оригинал codelab.ru источник
Шаблонные методы приводят к инвертированной структуре кода, которую иногда называют принципом Голливуда, подразумевая часто употребляемую в этой киноимперии фразу «Не звоните нам, мы сами позвоним». В данном случае это означает, что родительский класс сам вызывает операции подкласса, а не наоборот. оригинал codelab.ru codelab.ru источник
Шаблонные методы вызывают операции следующих видов: codelab.ru codelab.ru источник оригинал
  • конкретные операции (либо из класса ConcreteClass, либо из классов клиента). codelab.ru источник codelab.ru оригинал
  • конкретные операции из класса AbstractClass (то есть операции, полезные всем подклассам). оригинал codelab.ru codelab.ru источник
  • примитивные операции (то есть абстрактные операции). codelab.ru источник оригинал codelab.ru
  • фабричные методы (см. паттерн фабричный метод). оригинал источник codelab.ru codelab.ru
  • операции-зацепки (hook operations), реализующие поведение по умолчанию, которое может быть расширено в подклассах. Часто такая операция по умолчанию не делает ничего. codelab.ru источник оригинал codelab.ru
Важно, чтобы в шаблонном методе четко различались операции-зацепки (которые можно замещать) и абстрактные операции (которые нужно замещать). Чтобы повторно использовать абстрактный класс с максимальной эффективностью, авторы подклассов должны понимать, какие операции предназначены для замещения. Обычно об этом явно говорится и в документации к использованию и в комментариях непосредственно в коде. codelab.ru источник оригинал codelab.ru
Подкласс может расширить поведение некоторой операции, заместив ее и явно codelab.ru источник оригинал codelab.ru
вызвав эту операцию из родительского класса:
 пример [C++]  ссылка
  1. void DerivedClass::Operation () {
  2. ParentClass::Operation();
  3. // Расширенное поведение класса DerivedClass
  4. }
  5.  
codelab.ru codelab.ru источник оригинал
  codelab.ru codelab.ru источник оригинал
К сожалению, очень легко забыть о необходимости вызывать унаследованную операцию. У нас есть возможность трансформировать такую операцию в шаблонный метод с целью предоставить родителю контроль над тем, как подклассы расширяют его. Идея в том, чтобы вызывать операцию-зацепку из шаблонного метода в родительском классе. Тогда подклассы смогут переопределить именно эту операцию:
 пример [C++]  ссылка
  1. void ParentClass::Operation () {
  2. // Поведение родительского класса ParentClass
  3. HookOperation();
  4. }
  5.  
codelab.ru codelab.ru источник оригинал
  codelab.ru codelab.ru источник оригинал
В родительском же классе ParentClass эта операция HookOperation не делает ничего:
 пример [C++]  ссылка
  1. void ParentClass::HookOperation () { }
codelab.ru источник codelab.ru оригинал
  codelab.ru оригинал источник codelab.ru
Но она замещена в подклассах, которые расширяют поведение:
 пример [C++]  ссылка
  1. void DerivedClass::HookOperation () {
  2. // расширение в производном классе
  3. }
  4.  
источник codelab.ru codelab.ru оригинал
источник codelab.ru codelab.ru оригинал

Пример

Паттерн шаблонный метод широко применяется при написании разного рода каркасов. оригинал codelab.ru codelab.ru источник
Для примера рассмотрим основополагающий класс Struts веб-фреймворка: RequestProcessor. Метод этого класса process() запускается при обработке любого запроса на страницу, где задействуется Struts. Очевидно, что такого рода операция подразумевает четкую последовательность шагов обработки http-запроса, включающую все возможные разновидности алгоритма обработки, а также поддержку различных видов запроса и т.д. Но в то же время, чтобы данный фреймворк был довольно гибким необходимо дать возможность расширения этой базовой функциональности алгоритма, его модификации в разумных пределах: т.е. использование фабричных методов и операций-зацепок. Как видно – идеальный случай реализации шаблонного метода. Достаточно слов – просто взглянем на этот метод process():
 RequestProcessor.process() [java]  ссылка
  1. public void process(HttpServletRequest request, HttpServletResponse response)
  2. throws IOException, ServletException {
  3. // Wrap multipart requests with a special wrapper
  4. request = processMultipart(request);
  5.  
  6. // Identify the path component we will use to select a mapping
  7. String path = processPath(request, response);
  8.  
  9. if (path == null) {
  10. return;
  11. }
  12.  
  13. if (log.isDebugEnabled()) {
  14. log.debug("Processing a '" + request.getMethod() + "' for path '"
  15. + path + "'");
  16. }
  17.  
  18. // Select a Locale for the current user if requested
  19. processLocale(request, response);
  20.  
  21. // Set the content type and no-caching headers if requested
  22. processContent(request, response);
  23. processNoCache(request, response);
  24.  
  25. // General purpose preprocessing hook
  26. if (!processPreprocess(request, response)) {
  27. return;
  28. }
  29.  
  30. this.processCachedMessages(request, response);
  31.  
  32. // Identify the mapping for this request
  33. ActionMapping mapping = processMapping(request, response, path);
  34.  
  35. if (mapping == null) {
  36. return;
  37. }
  38.  
  39. // Check for any role required to perform this action
  40. if (!processRoles(request, response, mapping)) {
  41. return;
  42. }
  43.  
  44. // Process any ActionForm bean related to this request
  45. ActionForm form = processActionForm(request, response, mapping);
  46.  
  47. processPopulate(request, response, form, mapping);
  48.  
  49. // Validate any fields of the ActionForm bean, if applicable
  50. try {
  51. if (!processValidate(request, response, form, mapping)) {
  52. return;
  53. }
  54. } catch (InvalidCancelException e) {
  55. ActionForward forward = processException(request, response, e, form, mapping);
  56. processForwardConfig(request, response, forward);
  57. return;
  58. } catch (IOException e) {
  59. throw e;
  60. } catch (ServletException e) {
  61. throw e;
  62. }
  63.  
  64. // Process a forward or include specified by this mapping
  65. if (!processForward(request, response, mapping)) {
  66. return;
  67. }
  68.  
  69. if (!processInclude(request, response, mapping)) {
  70. return;
  71. }
  72.  
  73. // Create or acquire the Action instance to process this request
  74. Action action = processActionCreate(request, response, mapping);
  75.  
  76. if (action == null) {
  77. return;
  78. }
  79.  
  80. // Call the Action instance itself
  81. ActionForward forward =
  82. processActionPerform(request, response, action, form, mapping);
  83.  
  84. // Process the returned ActionForward instance
  85. processForwardConfig(request, response, forward);
  86. }
источник codelab.ru оригинал codelab.ru
  codelab.ru codelab.ru источник оригинал
Теперь просто по порядку: codelab.ru codelab.ru источник оригинал
  1. processMultipart() – фабричный метод задействования специальной обработки multipart запросов:
     RequestProcessor.processMultipart() [java]  ссылка
    1. protected HttpServletRequest processMultipart(HttpServletRequest request) {
    2. if (!"POST".equalsIgnoreCase(request.getMethod())) {
    3. return (request);
    4. }
    5.  
    6. String contentType = request.getContentType();
    7.  
    8. if ((contentType != null)
    9. && contentType.startsWith("multipart/form-data")) {
    10. return (new MultipartRequestWrapper(request));
    11. } else {
    12. return (request);
    13. }
    14. }
    источник оригинал codelab.ru codelab.ru
  2. processPath() – фабричный метод определения запрошенного пути ресурса:
     RequestProcessor.processPath() [java]  ссылка
    1. protected String processPath(HttpServletRequest request,
    2. HttpServletResponse response)
    3. throws IOException {
    4. String path;
    5.  
    6. // For prefix matching, match on the path info (if any)
    7. path = (String) request.getAttribute(INCLUDE_PATH_INFO);
    8.  
    9. if (path == null) {
    10. path = request.getPathInfo();
    11. }
    12.  
    13. if ((path != null) && (path.length() > 0)) {
    14. return (path);
    15. }
    16.  
    17. // For extension matching, strip the module prefix and extension
    18. path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
    19.  
    20. if (path == null) {
    21. path = request.getServletPath();
    22. }
    23.  
    24. String prefix = moduleConfig.getPrefix();
    25.  
    26. if (!path.startsWith(prefix)) {
    27. String msg = getInternal().getMessage("processPath");
    28.  
    29. log.error(msg + " " + request.getRequestURI());
    30. response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    31.  
    32. return null;
    33. }
    34.  
    35. path = path.substring(prefix.length());
    36.  
    37. int slash = path.lastIndexOf("/");
    38. int period = path.lastIndexOf(".");
    39.  
    40. if ((period >= 0) && (period > slash)) {
    41. path = path.substring(0, period);
    42. }
    43.  
    44. return (path);
    45. }
    codelab.ru источник оригинал codelab.ru
  3. processLocale() – фабричный метод получения локали обратившегося по http-пользователя:
     RequestProcessor.processLocale() [java]  ссылка
    1. protected void processLocale(HttpServletRequest request,
    2. HttpServletResponse response) {
    3. // Are we configured to select the Locale automatically?
    4. if (!moduleConfig.getControllerConfig().getLocale()) {
    5. return;
    6. }
    7.  
    8. // Has a Locale already been selected?
    9. HttpSession session = request.getSession();
    10.  
    11. if (session.getAttribute(Globals.LOCALE_KEY) != null) {
    12. return;
    13. }
    14.  
    15. // Use the Locale returned by the servlet container (if any)
    16. Locale locale = request.getLocale();
    17.  
    18. if (locale != null) {
    19. if (log.isDebugEnabled()) {
    20. log.debug(" Setting user locale '" + locale + "'");
    21. }
    22.  
    23. session.setAttribute(Globals.LOCALE_KEY, locale);
    24. }
    25. }
    оригинал codelab.ru источник codelab.ru
  4. processContent() – фабричный метод выставления Content-Type-а ответа сервера:
     RequestProcessor.processContent() [java]  ссылка
    1. protected void processContent(HttpServletRequest request,
    2. HttpServletResponse response) {
    3. String contentType =
    4. moduleConfig.getControllerConfig().getContentType();
    5.  
    6. if (contentType != null) {
    7. response.setContentType(contentType);
    8. }
    9. }
    оригинал источник codelab.ru codelab.ru
  5. processNoCache() – фабричный метод выставления заголовков запрета кеширования на стороне клиента (браузера):
     RequestProcessor.processNoCache() [java]  ссылка
    1. protected void processNoCache(HttpServletRequest request,
    2. HttpServletResponse response) {
    3. if (moduleConfig.getControllerConfig().getNocache()) {
    4. response.setHeader("Pragma", "No-cache");
    5. response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
    6. response.setDateHeader("Expires", 1);
    7. }
    8. }
    оригинал codelab.ru источник codelab.ru
  6. processPreprocess() – операция-зацепка для процедур общего назначения, потребность в которых может возникнуть именно в данном месте – после полной идентификации запроса и до запуска Action-а на выполнение:
     RequestProcessor.processPreprocess() [java]  ссылка
    1. protected boolean processPreprocess(HttpServletRequest request,
    2. HttpServletResponse response) {
    3. return (true);
    4. }
    источник codelab.ru codelab.ru оригинал

и т.д. источник codelab.ru codelab.ru оригинал

Дальше можно увидеть еще несколько зацепок: processRoles(), processValidate(), processForward() и processInclude(); и остальное все – фабричные методы: processMapping(), processActionForm(), processPopulate(), processActionCreate() и т.д. – все содержат продуманные реализации по-умалчанию, которые вполне подходят в большинстве случаев. Весь класс: RequestProcessor. оригинал codelab.ru codelab.ru источник
оригинал codelab.ru codelab.ru источник
codelab.ru источник оригинал codelab.ru
оригинал codelab.ru codelab.ru источник
источник оригинал codelab.ru codelab.ru

Известные применения паттерна Шаблонный метод (Template Method)

Шаблонные методы настолько фундаментальны, что встречаются почти в каждом абстрактном классе. Очень широко шаблонные методы используются в различных фреймворках, например, из самых известных это: Struts, Spring. codelab.ru источник оригинал codelab.ru
codelab.ru оригинал codelab.ru источник
codelab.ru источник оригинал codelab.ru

Родственные паттерны

Фабричные методы часто вызываются из шаблонных. В примере из раздела «Мотивация» шаблонный метод OpenDocument вызывал фабричный метод DoCreateDocument. источник codelab.ru codelab.ru оригинал
Стратегия: шаблонные методы применяют наследование для модификации части алгоритма. Стратегии используют делегирование для модификации алгоритма в целом. codelab.ru источник оригинал codelab.ru


Реализации: java(1)   +добавить реализацию

1) RequestProcessor.java, code #528[автор:this]


<< назад наверх
распечатать обсудить >>

 
каталог | задачи | паттерны | исходники | стат | форумы | карта сайта | контакты | ссылки 
© 2000-2017 CodeLAB Group
  Все права защищены
Страница сгенерирована за 0.040847 секунд
Количество запросов к БД: 14, gzip: 18.4kb/95.5kb(81%)