Паттерн «Стратегия» (Strategy)
Описание
Паттерн «Стратегия» позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми. Это позволяет изменять поведение объекта без изменения его класса.
Имеет схожую структуру с паттерном «Состояние». Построен на принципе «композиции», то есть на делегировании работы другим объектам.
«Стратегия» vs «Состояние»
В паттерне «Стратегия» различные стратегии работают независимо друг от друга и не взаимодействуют между собой.
В паттерне «Состояние» состояния могут менять друг друга, поскольку они связаны с общим объектом и управляют его переходами из одного состояния в другое.
Основные элементы паттерна:
- Контекст (Context): класс, который использует стратегию для выполнения определённой задачи. Контекст не знает, как работает конкретная стратегия, а просто вызывает метод, предоставленный стратегией.
- Интерфейс стратегии (Strategy): определяет интерфейс для всех возможных стратегий (алгоритмов).
- Конкретные стратегии (Concrete Strategy): классы, которые реализуют различные варианты поведения.
Пример: Оплата с помощью кредитных карт
У пользователя есть три кредитные карты:
UnionPay,MasterCardиVisa. Пользователь делит покупку на три части и оплачивает каждую часть с помощью одной из карт.
Интерфейс стратегии:
Каждая кредитная карта реализует интерфейс PaymentStrategy, который имеет метод pay.
interface PaymentStrategy {
pay(amount: number): boolean; // Метод возвращает true, если оплата успешна
}Конкретные стратегии:
Каждая карта будет иметь баланс, и мы будем проверять, достаточно ли средств для оплаты. Если достаточно — оплата проходит успешно, иначе она отклоняется.
class UnionPay implements PaymentStrategy {
private balance: number;
constructor(balance: number) {
this.balance = balance;
}
pay(amount: number): boolean {
if (this.balance >= amount) {
this.balance -= amount;
console.log(`Оплата ${amount} с UnionPay прошла успешно. Остаток: ${this.balance}`);
return true;
} else {
console.log(`Недостаточно средств на карте UnionPay.`);
return false;
}
}
}
class MasterCard implements PaymentStrategy {
private balance: number;
constructor(balance: number) {
this.balance = balance;
}
pay(amount: number): boolean {
if (this.balance >= amount) {
this.balance -= amount;
console.log(`Оплата ${amount} с MasterCard прошла успешно. Остаток: ${this.balance}`);
return true;
} else {
console.log(`Недостаточно средств на карте MasterCard.`);
return false;
}
}
}
class Visa implements PaymentStrategy {
private balance: number;
constructor(balance: number) {
this.balance = balance;
}
pay(amount: number): boolean {
if (this.balance >= amount) {
this.balance -= amount;
console.log(`Оплата ${amount} с Visa прошла успешно. Остаток: ${this.balance}`);
return true;
} else {
console.log(`Недостаточно средств на карте Visa.`);
return false;
}
}
}Контекст:
Контекст отвечает за процесс оплаты и может переключаться между разными стратегиями (картами) для оплаты каждой части покупки.
class PaymentProcessor {
private strategy: PaymentStrategy;
constructor(strategy: PaymentStrategy) {
this.strategy = strategy;
}
setStrategy(strategy: PaymentStrategy): void {
this.strategy = strategy;
}
processPayment(amount: number): boolean {
return this.strategy.pay(amount);
}
}Использование:
Теперь создадим пример, в котором пользователь будет делить покупку на три части и оплачивать их с помощью трёх карт.
// Инициализация карт с определёнными балансами
const unionPayCard = new UnionPay(100);
const masterCard = new MasterCard(150);
const visaCard = new Visa(200);
// Инициализация процессора оплаты
const processor = new PaymentProcessor(unionPayCard);
// Сумма покупки
const totalAmount = 270; // Например, покупка стоит 270 единиц
const partAmount = totalAmount / 3; // Каждая карта оплатит 1/3
// Оплата первой части UnionPay
console.log('Оплата первой части:');
processor.setStrategy(unionPayCard);
processor.processPayment(partAmount);
// Оплата второй части MasterCard
console.log('Оплата второй части:');
processor.setStrategy(masterCard);
processor.processPayment(partAmount);
// Оплата третьей части Visa
console.log('Оплата третьей части:');
processor.setStrategy(visaCard);
processor.processPayment(partAmount);