← Назад к вопросам
К какому паттерну относится шаблонный метод
2.0 Middle🔥 121 комментариев
#Архитектура и паттерны#ООП
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Template Method - паттерн проектирования
Шаблонный метод (Template Method) — это паттерн поведенческого проектирования, который определяет скелет алгоритма в базовом классе, но оставляет отдельные шаги (методы) для реализации в подклассах.
Характеристика паттерна
- Тип: Behavioral (поведенческий) паттерн
- Принцип: DRY (Don't Repeat Yourself) — избегаем дублирования кода
- Назначение: Позволить подклассам переопределить определённые шаги алгоритма, не изменяя структуру самого алгоритма
Структура паттерна Template Method
- Абстрактный базовый класс — определяет шаблонный метод и абстрактные методы
- Конкретные подклассы — реализуют абстрактные методы
- Клиент — работает с экземплярами подклассов
Примеры Template Method в Node.js
Пример 1: Обработка различных форматов данных
abstract class DataProcessor {
// Шаблонный метод - определяет скелет алгоритма
final process(data: string): void {
this.parse(data);
this.validate();
this.transform();
this.save();
}
// Абстрактные методы, которые должны быть реализованы подклассами
abstract parse(data: string): void;
abstract validate(): void;
abstract transform(): void;
abstract save(): void;
}
// Конкретная реализация для CSV
class CSVProcessor extends DataProcessor {
private data: any;
parse(data: string): void {
this.data = data.split('\n').map(line => line.split(','));
console.log('CSV parsed');
}
validate(): void {
console.log('CSV validation');
}
transform(): void {
console.log('CSV transformation');
}
save(): void {
console.log('CSV saved to database');
}
}
// Конкретная реализация для JSON
class JSONProcessor extends DataProcessor {
private data: any;
parse(data: string): void {
this.data = JSON.parse(data);
console.log('JSON parsed');
}
validate(): void {
console.log('JSON schema validation');
}
transform(): void {
console.log('JSON transformation');
}
save(): void {
console.log('JSON saved to database');
}
}
// Использование
const csvProcessor = new CSVProcessor();
const jsonProcessor = new JSONProcessor();
const csvData = 'name,age\nJohn,30\nJane,25';
const jsonData = '{"name":"John","age":30}';
csvProcessor.process(csvData); // Выведет все шаги для CSV
jsonProcessor.process(jsonData); // Выведет все шаги для JSON
Пример 2: API запросы с различными авторизациями
abstract class APIClient {
protected baseURL: string;
// Шаблонный метод
async fetch(endpoint: string, method: 'GET' | 'POST' = 'GET', body?: any) {
const url = this.buildURL(endpoint);
const headers = this.getHeaders();
const auth = await this.authenticate();
const response = await this.makeRequest(url, method, { ...headers, ...auth }, body);
return this.handleResponse(response);
}
// Абстрактные методы
abstract authenticate(): Promise<Record<string, string>>;
abstract getHeaders(): Record<string, string>;
// Конкретные методы, переиспользуемые всеми подклассами
protected buildURL(endpoint: string): string {
return `${this.baseURL}${endpoint}`;
}
protected async makeRequest(
url: string,
method: string,
headers: Record<string, string>,
body?: any,
) {
return fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined });
}
protected handleResponse(response: Response) {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
}
// Bearer Token авторизация
class BearerAPIClient extends APIClient {
constructor(private token: string) {
super();
this.baseURL = 'https://api.example.com';
}
async authenticate(): Promise<Record<string, string>> {
return { Authorization: `Bearer ${this.token}` };
}
getHeaders(): Record<string, string> {
return { 'Content-Type': 'application/json' };
}
}
// Basic Auth авторизация
class BasicAuthAPIClient extends APIClient {
constructor(private username: string, private password: string) {
super();
this.baseURL = 'https://api.example.com';
}
async authenticate(): Promise<Record<string, string>> {
const credentials = btoa(`${this.username}:${this.password}`);
return { Authorization: `Basic ${credentials}` };
}
getHeaders(): Record<string, string> {
return { 'Content-Type': 'application/json', 'User-Agent': 'MyApp/1.0' };
}
}
// API Key авторизация
class APIKeyClient extends APIClient {
constructor(private apiKey: string) {
super();
this.baseURL = 'https://api.example.com';
}
async authenticate(): Promise<Record<string, string>> {
return { 'X-API-Key': this.apiKey };
}
getHeaders(): Record<string, string> {
return { 'Content-Type': 'application/json' };
}
}
// Использование
const bearerClient = new BearerAPIClient('my-token-123');
const basicClient = new BasicAuthAPIClient('user', 'password');
const apiKeyClient = new APIKeyClient('sk-1234567890');
// Все клиенты используют один и тот же алгоритм fetch()
await bearerClient.fetch('/users');
await basicClient.fetch('/data');
await apiKeyClient.fetch('/products');
Пример 3: Pipeline обработки с NestJS
abstract class BaseService {
// Шаблонный метод - определяет общий workflow
async processRequest(data: any) {
console.log('1. Начало обработки');
const validated = await this.validate(data);
const processed = await this.process(validated);
const result = await this.finalize(processed);
console.log('4. Обработка завершена');
return result;
}
// Методы, которые могут быть переопределены
protected async validate(data: any): Promise<any> {
console.log('2. Валидация (базовая)');
return data;
}
protected async process(data: any): Promise<any> {
console.log('3. Обработка (базовая)');
return data;
}
protected async finalize(data: any): Promise<any> {
console.log('4. Финализация (базовая)');
return data;
}
}
@Injectable()
class UserService extends BaseService {
protected async validate(data: any): Promise<any> {
console.log('2. Валидация email')
if (!data.email) throw new BadRequestException('Email обязателен');
return data;
}
protected async process(data: any): Promise<any> {
console.log('3. Хеширование пароля')
data.password = await bcrypt.hash(data.password, 10);
return data;
}
protected async finalize(data: any): Promise<any> {
console.log('4. Сохранение в БД')
return this.repository.save(data);
}
}
@Injectable()
class ProductService extends BaseService {
protected async validate(data: any): Promise<any> {
console.log('2. Проверка цены')
if (data.price < 0) throw new BadRequestException('Цена не может быть отрицательной');
return data;
}
protected async process(data: any): Promise<any> {
console.log('3. Расчёт налогов')
data.taxAmount = data.price * 0.18;
return data;
}
}
Преимущества Template Method
- DRY принцип — избегаем дублирования общего кода
- Инверсия управления — подклассы контролируют конкретные шаги
- Прозрачность — алгоритм виден в базовом классе
- Масштабируемость — легко добавлять новые реализации
- Консистентность — гарантирует правильный порядок шагов
Когда использовать Template Method
- Когда есть общий алгоритм, но шаги отличаются
- Когда нужно избежать дублирования кода в подклассах
- Когда порядок выполнения критичен
- В обработке данных (парсинг, валидация, трансформация)
- В API клиентах с разными авторизациями
- В middleware цепочках
Отличие от Strategy
Template Method: Алгоритм в базовом классе, подклассы переопределяют шаги
Strategy: Алгоритмы в отдельных классах, клиент выбирает нужный
Template Method — это поведенческий паттерн, который определяет скелет алгоритма в базовом классе, позволяя подклассам переопределять отдельные шаги без изменения структуры алгоритма.