Обидва патерни розв'язують одну задачу - змінювати частину алгоритму, не змінюючи загальної структури. Різниця - у механізмі.
Template Method - через успадкування. Базовий клас задає скелет алгоритму, а змінні кроки - абстрактні методи для нащадків:
abstract class DataImport
{
final public function run(string $path): ImportResult
{
$rows = $this->read($path); // змінний крок
$valid = array_filter($rows, $this->validate(...));
$this->persist($valid); // змінний крок
return new ImportResult(count($valid), count($rows) - count($valid));
}
abstract protected function read(string $path): array;
abstract protected function validate(array $row): bool;
abstract protected function persist(array $rows): void;
}
final class ProductImport extends DataImport { /* реалізує три кроки */ }
Strategy - через композицію. Змінна частина - окремий об'єкт, переданий ззовні:
final class DataImport
{
public function __construct(
private RowReader $reader,
private RowValidator $validator,
private RowWriter $writer,
) {}
public function run(string $path): ImportResult { /* той самий скелет */ }
}
Порівняння:
| Template Method | Strategy | |
|---|---|---|
| механізм | успадкування | композиція |
| коли обирається варіант | при створенні класу (компіляція) | при створенні об'єкта (виконання) |
| комбінування варіантів | новий підклас на кожну комбінацію | будь-яка комбінація об'єктів |
| доступ до стану | нащадок бачить protected батька |
лише через параметри й інтерфейс |
| тестування кроків | через підклас | кожна стратегія окремо |
Коли Template Method доречний:
- скелет стабільний, варіантів небагато, кроки тісно пов'язані між собою;
- фреймворк визначає життєвий цикл, а ви заповнюєте «гачки»:
Command::handle(),Seeder::run(),Notification::via()/toMail()у Laravel,setUp()у тестах - усе це шаблонні методи; - кроки потребують спільного стану базового класу.
Коли Strategy:
- варіанти комбінуються незалежно (формат × доставка × кешування);
- варіант обирається під час виконання (за налаштуваннями, типом користувача);
- кроки корисно тестувати й повторно використовувати окремо.
Еволюція: часто код починається з Template Method (простіше), а коли комбінацій стає багато - рефакториться в Strategy («замінити успадкування делегуванням»). final на методі-скелеті в Template Method важливий - нащадки не повинні змінювати порядок кроків.
Докладніше в документації: Refactoring.Guru: Template Method