Паттерн «Шаблонный метод» (Template Method) — это поведенческий ООП-паттерн, в котором базовый класс определяет «скелет» алгоритма, а подклассы переопределяют отдельные его шаги, не меняя общей структуры. В C# это обычно реализуется как абстрактный (или частично абстрактный) класс с финальным публичным методом, внутри которого вызываются виртуальные/абстрактные методы-шаги.bool+1
Что такое «Шаблонный метод»
Назначение:
Определить шаблон (каркас) алгоритма в базовом классе.
Выделить шаги, которые могут различаться в подклассах.
Сделать эти шаги виртуальными или абстрактными, чтобы наследники могли их переопределять.radioprog+1
Классическая структура:
textpublic abstract class Processor { // Шаблонный метод — финальный, клиенты работают только с ним public void Process() { Prepare(); var data = LoadData(); Transform(data); SaveData(data); Cleanup(); } // Стандартный шаг — можно переопределять, но не обязательно protected virtual void Prepare() { // базовая логика } // Обязательный шаг — переопределяется в подклассах protected abstract long LoadData(); // optional шаг protected virtual void Transform(long data) { // базовая трансформация } // another обязательный шаг protected abstract void SaveData(long data); // cleanup protected virtual void Cleanup() { // базовая очистка } } public class FileProcessor : Processor { protected override long LoadData() { // читаем из файла return 123; } protected override void SaveData(long data) { // записываем в файл } protected override void Transform(long data) { // специфичная трансформация для файлов } }
Клиент работает только с Process(), а детали реализации шагов скрыты в подклассах.bool+1
Плюсы паттерна
Устранение дублирования кода
Общий алгоритм вынесен в базовый класс, подклассы содержат только отличия.boolЕдиная точка контроля порядка шагов
Порядок выполнения шагов фиксируется в шаблонном методе, его нельзя случайно «перепутать» в подклассах.radioprogПростое расширение
Чтобы добавить новую вариацию алгоритма, нужно просто создать новый подкласс и переопределить нужные шаги.boolИнкапсуляция алгоритма
Клиенты не знают деталей реализации шагов, взаимодействуют только с шаблонным методом.radioprog
Минусы паттерна
Нарушение принципа инверсии зависимостей (в классическом виде)
Подклассы зависят от базового класса, а не наоборот; изменение базового класса может ломать много наследников.radioprogСложность при большом числе шагов
Если алгоритм многошаговый и с множеством вариантов, абстрактный класс может стать громоздким и трудным для понимания.radioprogОграниченная гибкость по сравнению с «Стратегией»
Шаблонный метод работает через наследование, а не через передачу объектов-стратегий; изменить поведение в runtime сложнее.boolРиск «тяжелых» базовых классов
При частом дроблении шагов и их вариаций базовый класс может превращаться в «монолит» с множеством виртуальных методов.radioprog
Когда и где применяется
Паттерн удобен, когда:
Есть схожие алгоритмы с одинаковой общей структурой, но разными деталями отдельных шагов.bool
Нужно фиксировать порядок шагов и гарантировать, что подклассы не будут его нарушать.radioprog
В будущем планируется создавать много подклассов, каждый со своей вариацией алгоритма.bool
В коде есть дублирование похожих алгоритмов в разных классах — их можно объединить в шаблонный метод.bool
Примеры использования в .NET:
WCF: классы вроде
CommunicationObject,ChannelBase,ServiceHostBaseиспользуют шаблонный метод: публичные методыOpen,Closeи т.п. sealed, но внутри вызывают виртуальные/абстрактныеOnOpen,OnClose.boolЛюбой абстрактный класс с защищёнными абстрактными/виртуальными методами, которые вызываются из общего публичного метода, фактически является примером Template Method.bool
Утратил ли паттерн актуальность в C#
Нет, паттерн не утратил актуальность, но его применение стало более осознанным:
В современных подходах (чистая архитектура, композиция вместо наследования) часто предпочитают стратегию + делегирование вместо глубоких базовых классов с множеством виртуальных шагов.jasulib
Однако для фреймворков и базовых классов (например, сервисы обработки, workflow-механизмы) Template Method остаётся удобным способом предоставить «скелет» алгоритма иAllowing пользователям переопределять детали.jasulib+1
В .NET Aspire, Dapr-подобных архитектурах и библиотечных абстракциях этот паттерн всё ещё активно используется для построения гибких, но контролируемых алгоритмов.jasulib
Таким образом, паттерн остался важным инструментом, особенно в библиотека и фреймворках, но в простых приложениях его часто заменяют более композиционными решениями.
Примеры кода в контексте .NET
1. Обработка данных (файл, блог, API)
textpublic abstract class DataPipeline<T> { public void Run() { var source = LoadSource(); var items = Parse(source); items = Transform(items); Save(items); } protected abstract T LoadSource(); protected abstract IEnumerable<string> Parse(T source); protected virtual IEnumerable<string> Transform(IEnumerable<string> items) => items; protected abstract void Save(IEnumerable<string> items); } public class FilePipeline : DataPipeline<string> { protected override string LoadSource() { // чтение файла return System.IO.File.ReadAllText("input.txt"); } protected override IEnumerable<string> Parse(string source) { return source.Split('\n'); } protected override void Save(IEnumerable<string> items) { System.IO.File.WriteAllLines("output.txt", items); } }
2.wurf-сервис (веб, консоль, тест)
textpublic abstract class ReportService { public void GenerateReport() { var data = CollectData(); Format(data); Export(data); } protected abstract IEnumerable<DataItem> CollectData(); protected virtual void Format(IEnumerable<DataItem> data) { } protected abstract void Export(IEnumerable<DataItem> data); } public class WebReportService : ReportService { protected override IEnumerable<DataItem> CollectData() { // сбор данных из API / БД return new List<DataItem>(); } protected override void Export(IEnumerable<DataItem> data) { // возврат HTML / JSON клиенту } }
Эти примеры показывают типичное применение: один публичный метод, набор шагов, часть обязательных, часть переопределяемых по желанию.radioprog+1
Если кратко:
Шаблонный метод — это «каркас алгоритма + переопределяемые шаги».
Плюсы: устранение дублирования, контроль порядка, простое расширение.
Минусы: сильная связь через наследование, риск громоздких базовых классов, меньше гибкости чем у Стратегии.
В современных .NET-приложениях он всё ещё актуален, особенно в фреймворках и библиотеках, но в простых сценариях часто заменяется композицией и делегированием.