Se você já se pegou pensando “putz, esse if/else aqui tá crescendo demais” ou “cada nova regra quebra algo que já funcionava”, é bem provável que o padrão Strategy seja exatamente o que você está procurando.
Neste post, vamos falar:
- O que é o Strategy
- Qual problema ele resolve
- Quando usar (e quando NÃO usar)
- Exemplos práticos
O problema clássico
Imagine um sistema que precisa executar uma ação de formas diferentes dependendo do contexto:
- Tipos diferentes de cálculo
- Regras que mudam por ambiente (QA, prod)
- Variações de comportamento por cliente, país ou feature
O caminho mais comum (e perigoso) costuma ser algo assim:
if tipo == A {
faz isso
} else if tipo == B {
faz aquilo
} else if tipo == C {
faz outra coisa
}
Funciona? Funciona.
Escala? Nem um pouco 😅
Cada nova regra:
- aumenta a complexidade
- quebra o princípio aberto/fechado (Open/Closed)
- transforma a função num monstro difícil de testar
O que é o padrão Strategy
O Strategy é um padrão de projeto comportamental que resolve exatamente esse problema.
A ideia central é simples:
Encapsular comportamentos intercambiáveis atrás de uma interface comum
Ou seja:
- você define o que precisa ser feito (interface)
- cria várias formas de fazer isso (strategies)
- escolhe qual usar em tempo de execução
Sem if/else espalhado.
Sem acoplamento desnecessário.
Estrutura do Strategy
Conceitualmente, temos três peças:
- Strategy (interface) – define o contrato
- Concrete Strategies – implementações do comportamento
- Context – quem usa a strategy
Visualmente:
Context -> Strategy
↑
-------------------------
| | |
StrategyA StrategyB StrategyC
Exemplo prático
Vamos imaginar um cenário simples:
cálculo de taxa.
Interface
type TaxStrategy interface {
Calculate(value float64) float64
}
Implementações
type BrazilTax struct{}
func (b *BrazilTax) Calculate(value float64) float64 {
return value * 0.15
}
type USATax struct{}
func (u *USATax) Calculate(value float64) float64 {
return value * 0.08
}
Context
type Order struct {
Tax TaxStrategy
}
func (o *Order) FinalValue(value float64) float64 {
return value + o.Tax.Calculate(value)
}
Uso
order := Order{Tax: &BrazilTax{}}
final := order.FinalValue(100)
Trocar a regra não exige mudar o Order.
Só trocar a strategy.
Registry de strategies
Em sistemas maiores, é comum usar um registry para centralizar as strategies disponíveis:
var registry = map[string]TaxStrategy{
"br": &BrazilTax{},
"us": &USATax{},
}
Isso facilita:
- extensão do sistema
- leitura do código
- configuração por ambiente ou feature
Quando usar Strategy
Use Strategy quando:
✅ Você tem variações de comportamento
✅ Essas variações crescem com o tempo
✅ Você quer eliminar if/else baseados em tipo, regra ou contexto
✅ Testabilidade é importante
Quando NÃO usar
Nem tudo precisa de Strategy 👀
Evite quando:
❌ Existe só uma implementação e nenhuma chance real de variação
❌ A abstração adiciona mais complexidade do que resolve
❌ O comportamento nunca muda
Padrão de projeto não é troféu — é ferramenta.
Erro comum ao usar Strategy
Um erro clássico é deixar a strategy conhecer o contexto errado, como ambiente:
if env == "qa" {
// lógica especial
}
Se isso começa a aparecer, é sinal de que:
- você precisa de strategies diferentes
- ou de comportamentos injetáveis
Strategy boa é pura e focada em uma única responsabilidade.
Conclusão
O padrão Strategy é um dos padrões mais úteis no dia a dia, especialmente em sistemas que:
- evoluem rápido
- têm regras de negócio mutáveis
- precisam ser fáceis de testar e manter
Se você sente que seu código está virando uma árvore de if/else, talvez não seja falta de esforço
Talvez só esteja faltando uma boa Estratégia 😉
Top comments (0)