Шаблонный метод (Template Method) определяет общий алгоритм поведения подклассов, позволяя им переопределить отдельные шаги этого алгоритма без изменения его структуры.
Когда использовать шаблонный метод?
- Когда планируется, что в будущем подклассы должны будут переопределять различные этапы алгоритма без изменения его структуры
- Когда в классах, реализующим схожий алгоритм, происходит дублирование кода. Вынесение общего кода в шаблонный метод уменьшит его дублирование в подклассах.
Формальная реализация паттерна на C#:
abstract class Template
{
public abstract void TemplateMethod();
}
abstract class AbstractClass
{
public sealed override void TemplateMethod()
{
Operation1();
Operation2();
}
public abstract void Operation1();
public abstract void Operation2();
}
class ConcreteClass : AbstractClass
{
public override void Operation1()
{
}
public override void Operation2()
{
}
}
Участники
- AbstractClass: определяет шаблонный метод
TemplateMethod(), который реализует алгоритм. Алгоритм может состоять из последовательности вызовов других методов, часть из которых может быть абстрактными и должны быть переопределены в классах-наследниках. При этом сам методTemplateMethod(), представляющий структуру алгоритма, переопределяться не должен. Поэтому в данном случае мы можем объявить его с модификатором sealed для закрытия от переопределения в подклассах. - ConcreteClass: подкласс, который может переопределять различные методы родительского класса.
Правда тут надо отметить, что чтобы сокрыть алгоритм от изменения в классах наследниках, метод объявляется с ключевым словом sealed. Но так как sealed используется только в связке с другим ключевым словом override, то необходимо абстрактный класс унаследовать от еще одного класса, в котором объявлен шаблонный метод.
Рассмотрим применение на конкретном примере. Допустим, в нашей программе используются объекты, представляющие учебу в школе и в вузе:
class School
{
// идем в первый класс
public void Enter() { }
// обучение
public void Study() { }
// сдаем выпускные экзамены
public void PassExams() { }
// получение аттестата об окончании
public void GetAttestat() { }
}
class University
{
// поступление в университет
public void Enter() { }
// обучение
public void Study() { }
// проходим практику
public void Practice() { }
// сдаем выпускные экзамены
public void PassExams() { }
// получение диплома
public void GetDiploma() { }
}
Как видно, эти классы очень похожи, и самое главное, реализуют примерно общий алгоритм. Да, где-то будет отличаться реализация методов, где-то чуть больше методов, но в целом мы имеем общий алгоритм, а функциональность обоих классов по большому счету дублируется. Поэтому для улучшения структуры классов мы могли бы применить шаблонный метод:
class Program
{
static void Main(string[] args)
{
School school = new School();
University university = new University();
school.Learn();
university.Learn();
Console.Read();
}
}
abstract class Learning
{
public abstract void Learn();
}
abstract class Education :Learning
{
public sealed override void Learn()
{
Enter();
Study();
PassExams();
GetDocument();
}
public abstract void Enter();
public abstract void Study();
public virtual void PassExams()
{
Console.WriteLine("Сдаем выпускные экзамены");
}
public abstract void GetDocument();
}
class School : Education
{
public override void Enter()
{
Console.WriteLine("Идем в первый класс");
}
public override void Study()
{
Console.WriteLine("Посещаем уроки, делаем домашние задания");
}
public override void GetDocument()
{
Console.WriteLine("Получаем аттестат о среднем образовании");
}
}
class University : Education
{
public override void Enter()
{
Console.WriteLine("Сдаем вступительные экзамены и поступаем в ВУЗ");
}
public override void Study()
{
Console.WriteLine("Посещаем лекции");
Console.WriteLine("Проходим практику");
}
public override void PassExams()
{
Console.WriteLine("Сдаем экзамен по специальности");
}
public override void GetDocument()
{
Console.WriteLine("Получаем диплом о высшем образовании");
}
}
При этом в базовом классе необязательно определять все методы алгоритма как абстрактные. Можно определить реализацию методов по умолчанию, как в случае с методом PassExams().
И в результате работы программы консоль выведет:
Идем в первый класс Посещаем уроки, делаем домашние задания Сдаем выпускные экзамены Получаем аттестат о среднем образовании Сдаем вступительные экзамены и поступаем в ВУЗ Посещаем лекции Проходим практику Сдаем экзамен по специальности Получаем диплом о высшем образовании