Strategy
O Strategy permite definir diferentes algoritmos ou estratégias, encapsulá-los em classes separadas e trocar entre elas dinamicamente.
A ideia é:
Separar o algoritmo de quem utiliza o algoritmo.
Imagine um sistema de pagamento:
Pagamento
├── Cartão
├── Pix
└── PayPal
Uma implementação problemática seria:
class Payment {
pay(
type: string,
amount: number
) {
if (type === "pix") {
// Pix
}
if (type === "card") {
// Cartão
}
if (type === "paypal") {
// PayPal
}
}
}
Conforme adicionamos métodos, a classe cresce.
Criando as estratégias
Criamos uma interface:
interface PaymentStrategy {
pay(amount: number): void;
}
Pix:
class PixPayment
implements PaymentStrategy {
pay(amount: number) {
console.log(
`Pagamento Pix: R$ ${amount}`
);
}
}
Cartão:
class CardPayment
implements PaymentStrategy {
pay(amount: number) {
console.log(
`Pagamento cartão: R$ ${amount}`
);
}
}
PayPal:
class PayPalPayment
implements PaymentStrategy {
pay(amount: number) {
console.log(
`Pagamento PayPal: R$ ${amount}`
);
}
}
Agora criamos o contexto:
class Checkout {
constructor(
private strategy:
PaymentStrategy
) {}
setStrategy(
strategy: PaymentStrategy
) {
this.strategy = strategy;
}
pay(amount: number) {
this.strategy.pay(
amount
);
}
}
Uso:
const checkout =
new Checkout(
new PixPayment()
);
checkout.pay(100);
Podemos trocar a estratégia:
checkout.setStrategy(
new CardPayment()
);
checkout.pay(100);
Ou:
checkout.setStrategy(
new PayPalPayment()
);
checkout.pay(100);
O Checkout não precisa conhecer os detalhes de cada método.
Exemplo prático: cálculo de frete
Podemos ter:
Frete
├── Correios
├── Transportadora
└── Retirada
Cada estratégia implementa:
interface ShippingStrategy {
calculate(
distance: number
): number;
}
Por exemplo:
class CorreiosShipping
implements ShippingStrategy {
calculate(
distance: number
) {
return distance * 1.5;
}
}
E:
class ExpressShipping
implements ShippingStrategy {
calculate(
distance: number
) {
return distance * 3;
}
}
O sistema pode escolher dinamicamente:
const shipping =
new ShippingCalculator(
new ExpressShipping()
);
Strategy vs State
Strategy e State são parecidos porque ambos utilizam composição e objetos que implementam uma interface.
A diferença principal está na intenção.
Strategy:
Escolher um algoritmo
Exemplo:
Pagamento via Pix
Pagamento via Cartão
State:
Comportamento muda conforme o estado
Exemplo:
Pedido Pendente
Pedido Aprovado
Pedido Enviado
Em Strategy, normalmente o código cliente escolhe a estratégia.
Em State, o próprio objeto pode mudar de estado durante sua execução.
Quando usar?
Strategy é útil quando:
- Existem várias formas de realizar uma operação.
- Queremos trocar algoritmos dinamicamente.
- Temos muitos
if/elserelacionados a regras. - Queremos facilitar testes e extensibilidade.
Exemplos:
Pagamentos
Frete
Ordenação
Autenticação
Descontos
Compressão
Algoritmos
A ideia principal é:
Encapsular algoritmos intercambiáveis e permitir escolher qual deles utilizar.