Стратегия (Strategy)
Strategy выносит взаимозаменяемые варианты алгоритма за общий интерфейс. Context делегирует вычисление выбранной стратегии и не содержит цепочку if по каждому варианту.
Пример
public interface DistributionStrategy {
List<BigDecimal> distribute(
BigDecimal total,
int parts
);
}
public final class EqualDistribution
implements DistributionStrategy {
@Override
public List<BigDecimal> distribute(
BigDecimal total,
int parts
) {
// алгоритм равного распределения
return List.of();
}
}Context получает strategy через constructor:
public final class DistributionService {
private final DistributionStrategy strategy;
public DistributionService(
DistributionStrategy strategy
) {
this.strategy = strategy;
}
public List<BigDecimal> calculate(
BigDecimal total,
int parts
) {
return strategy.distribute(total, parts);
}
}Выбор может происходить при конфигурации object graph или во время запроса:
DistributionStrategy strategy = switch (mode) {
case EQUAL -> new EqualDistribution();
case WEIGHTED -> new WeightedDistribution();
};Сам switch не исчезает обязательно. Он перемещается в одну точку выбора, а реализации алгоритмов остаются отдельными.
Когда полезна
- варианты имеют общий input/output contract;
- каждый алгоритм содержит самостоятельную логику;
- варианты нужно тестировать отдельно;
- выбор меняется без изменения context;
- условная ветка начала расти в нескольких местах.
Для двух коротких stateless операций отдельная иерархия классов может быть лишней. В Java стратегией может быть обычная функция:
UnaryOperator<BigDecimal> rounding =
value -> value.setScale(0, RoundingMode.DOWN);Strategy и Template Method
| Strategy | Шаблонный метод Template Method |
|---|---|
| композиция | наследование |
| заменяется весь алгоритм | заменяются отдельные steps |
| вариант можно выбрать runtime | вариант задаёт subclass |
| context делегирует | base class управляет порядком |
Типичные ошибки
- interface слишком широкий и заставляет варианты игнорировать параметры;
- context знает внутренние детали конкретной strategy;
- strategy хранит mutable state и неожиданно разделяется потоками;
- новый класс создаётся для однострочной функции без причины;
- выбор размазан по controllers вместо одной factory/configuration.
Вопросы для собеседования
- Что меняется независимо? Реализация алгоритма.
- Обязателен ли interface? Нет, роль может выполнять functional interface.
- Исчезает ли условный выбор? Не обязательно; он концентрируется в месте сборки.
- Главное отличие от Template Method? Композиция вместо наследования и замена алгоритма целиком.
- Когда паттерн избыточен? Когда вариант один или операция выражается простой функцией.