В объектно-ориентированном программировании метод шаблона является одним из поведенческих шаблонов проектирования, определенных Гаммой и др. [1] в книге Шаблоны проектирования . Метод шаблона является методом в суперклассе, обычно абстрактном суперклассе, и определяет скелет операции в терминах ряда высокоуровневых шагов. Эти шаги сами по себе реализуются дополнительными вспомогательными методами в том же классе, что и метод шаблона .
Вспомогательные методы могут быть либо абстрактными методами , в этом случае подклассы должны предоставлять конкретные реализации, либо методами-ловушками , которые имеют пустые тела в суперклассе. Подклассы могут (но не обязаны) настраивать операцию , переопределяя методы-ловушки. Целью метода-шаблона является определение общей структуры операции, позволяя подклассам уточнять или переопределять определенные шаги. [2]
Этот шаблон состоит из двух основных частей:
Во время выполнения алгоритм, представленный шаблонным методом, выполняется путем отправки шаблонного сообщения экземпляру одного из конкретных подклассов. Через наследование шаблонный метод в базовом классе начинает выполняться. Когда шаблонный метод отправляет сообщение самому себе, запрашивая один из вспомогательных методов, сообщение будет получено конкретным подэкземпляром. Если вспомогательный метод был переопределен, будет выполнена переопределяющая реализация в подэкземпляре; если он не был переопределен, будет выполнена унаследованная реализация в базовом классе. Этот механизм гарантирует, что общий алгоритм каждый раз следует тем же шагам, позволяя деталям некоторых шагов зависеть от того, какой экземпляр получил исходный запрос на выполнение алгоритма.
Этот шаблон является примером инверсии управления , поскольку высокоуровневый код больше не определяет, какие алгоритмы следует запускать; вместо этого во время выполнения выбирается алгоритм более низкого уровня.
Некоторые из сообщений, отправляемых шаблонным методом, могут быть методами- перехватчиками . Эти методы реализованы в том же базовом классе, что и шаблонный метод, но с пустыми телами (т. е. они ничего не делают). Методы-перехватчики существуют для того, чтобы подклассы могли переопределять их и, таким образом, настраивать действие алгоритма без необходимости переопределять сам шаблонный метод. Другими словами, они предоставляют «крючок», на который «подвешиваются» реализации вариантов.
В приведенной выше диаграмме классов UML определяется операция , которая определяет скелет (шаблон) поведенияAbstractClass
templateMethod()
primitive1()
и primitive2()
, которые, поскольку они реализованы в SubClass1
, позволяют этому подклассу предоставлять вариантную реализацию этих частей алгоритма.Метод шаблона используется в фреймворках, где каждый реализует инвариантные части архитектуры домена, предоставляя при этом методы-ловушки для настройки. Это пример инверсии управления . Метод шаблона используется по следующим причинам. [3]
Шаблон шаблона полезен при работе с автоматически сгенерированным кодом. Проблема работы с сгенерированным кодом заключается в том, что изменения в исходном коде приведут к изменениям в сгенерированном коде; если в сгенерированный код были внесены рукописные изменения, они будут утеряны. Как же тогда следует настраивать сгенерированный код?
Шаблон Template предоставляет решение. Если сгенерированный код следует шаблону метода шаблона, то весь сгенерированный код будет абстрактным суперклассом. При условии, что рукописные настройки ограничены подклассом, генератор кода может быть запущен снова без риска перезаписи этих изменений. При использовании с генерацией кода этот шаблон иногда называют шаблоном разрыва генерации . [7]
Данная реализация C++14 основана на реализации, существовавшей до C++98 и описанной в книге.
#include <iostream> #include <память> class View { // AbstractClass public : // определяет абстрактные примитивные операции, которые определяются конкретными подклассами для реализации шагов алгоритма. virtual void doDisplay () {} // реализует шаблонный метод, определяющий скелет алгоритма. Шаблонный метод вызывает примитивные операции, а также операции, определенные в AbstractClass или других объектах. void display () { setFocus (); doDisplay (); resetFocus (); } virtual ~ View () = default ; private : void setFocus () { std :: cout << "View::setFocus \n " ; } void resetFocus () { std :: cout << "View::resetFocus \n " ; } }; class MyView : public View { // ConcreteClass // реализует примитивные операции для выполнения шагов алгоритма, специфичных для подкласса. void doDisplay () override { // визуализирует содержимое представления std :: cout << "MyView::doDisplay \n " ; } }; int main () { // Умные указатели предотвращают утечки памяти std :: unique_ptr < View > myview = std :: make_unique < MyView > (); myview -> display (); }
Вывод программы:
Вид :: setFocus MyView :: doDisplay Вид :: resetFocus
Шаблон метода используется в фреймворках.