Strategy (Estratégia) é um padrão de design comportamental que define uma família de algoritmos intercambiáveis e coloca cada um deles em uma classe separada (Strategy). O padrão permite selecionar um algoritmo em tempo de execução: o código cliente funciona através de uma interface Strategy comum, e a implementação específica é substituída em tempo de execução. No iOS, o padrão é implementado via Protocol + classes de estratégia, no Android — via Interface + implementações. Strategy é um dos 23 padrões GoF, amplamente utilizado para processamento de pagamentos, validação, ordenação e filtragem de dados. Para mais detalhes — veja a descrição original do GoF.
Principais pontos
Strategy é um dos 23 padrões GoF (Gang of Four), descrito no livro "Design Patterns: Elements of Reusable Object-Oriented Software" (1994). O padrão resolve o problema de selecionar um algoritmo em tempo de execução. Em vez de escrever uma única classe com múltiplas declarações condicionais (if-else, switch), Strategy propõe extrair cada algoritmo em uma classe separada com uma interface comum. O contexto (a classe que usa a estratégia) mantém uma referência à interface Strategy e delega a execução à estratégia concreta.
A estrutura do padrão inclui três elementos: Context mantém uma referência à Strategy e chama seu método; Strategy (interface) declara um método comum para todos os algoritmos; ConcreteStrategy implementa a interface e contém o algoritmo concreto. O cliente cria a estratégia desejada e a passa ao contexto através de um construtor, setter ou parâmetro de método. O contexto não sabe qual estratégia específica está sendo executada — ele apenas trabalha com a interface.
| Componente | Função | Exemplo |
|---|---|---|
| Context | Mantém uma referência à Strategy | PaymentProcessor, Sorter |
| Strategy | Interface comum para algoritmos | Protocol PaymentStrategy |
| ConcreteStrategy | Implementação concreta do algoritmo | CardPayment, PayPalPayment |
Princípio Open/Closed — a principal vantagem do Strategy. O sistema está aberto para extensão (nova estratégia pode ser adicionada) e fechado para modificação (o código do contexto não precisa mudar). Sem o padrão, adicionar um novo algoritmo requer alterar a classe existente, o que viola o OCP e aumenta o risco de erros de regressão. O Strategy também reduz o tamanho das classes: em vez de uma classe de 200 linhas com switch-case, você obtém 6 classes de 20 linhas cada.
Strategy em Swift é implementado via Protocol (interface de estratégia) e classes ou structs de estratégia. Os protocolos Swift suportam tipos associados e restrições genéricas, proporcionando flexibilidade ao projetar estratégias. O contexto geralmente é uma classe ViewModel ou serviço que aceita a estratégia no init ou através de uma propriedade. O padrão é amplamente utilizado em projetos iOS para manipulação de eventos, animações, formatação de dados e estratégias de UI.
// 1. Protocol Strategy
protocol PaymentStrategy {
func pay(amount: Decimal) async throws -> PaymentResult
}
// 2. Concrete Strategies
struct CardPaymentStrategy: PaymentStrategy {
let cardNumber: String
let cvv: String
func pay(amount: Decimal) async throws -> PaymentResult {
// Enviando requisição para API bancária
return PaymentResult(status: .success, transactionId: "tx_\(UUID())")
}
}
struct PayPalPaymentStrategy: PaymentStrategy {
let email: String
func pay(amount: Decimal) async throws -> PaymentResult {
// Redirecionar para PayPal SDK
return PaymentResult(status: .success, transactionId: "pp_\(UUID())")
}
}
// 3. Context
class PaymentProcessor {
private var strategy: PaymentStrategy
init(strategy: PaymentStrategy) {
self.strategy = strategy
}
func setStrategy(_: PaymentStrategy) {
strategy = strategy
}
func processPayment(amount: Decimal) async throws -> PaymentResult {
return try await strategy.pay(amount: amount)
}
}
// Uso
let processor = PaymentProcessor(strategy: CardPaymentStrategy(cardNumber: "4111...", cvv: "123"))
let result = try await processor.processPayment(amount: 99.99)
Strategy no SwiftUI — o padrão se integra naturalmente com MVVM. Um ViewModel contém uma propriedade de estratégia e chama seu método mediante ação do usuário. A View do SwiftUI recebe dados via @Published ou @State — a estratégia oculta detalhes de implementação da View. Por exemplo, uma estratégia de validação de texto (emailValidator, phoneValidator) é trocada dependendo do tipo de campo de entrada. Combinar Strategy com SwiftUI oferece flexibilidade sem herdar de UIKit.
Strategy em Kotlin usa Interface no nível da linguagem e interfaces funcionais (SAM) para simplificação. Kotlin suporta lambdas, permitindo passar algoritmos como funções sem declarar uma classe de estratégia separada. No Android, o padrão é usado em ViewModel e Use Cases para isolar algoritmos de carregamento de dados, cache e tratamento de erros. Projetos Android com Clean Architecture usam Strategy para injetar diferentes implementações de repositório dependendo de flags (mock, real, cache).
// 1. Interface Strategy
interface PaymentStrategy {
suspend fun pay(amount: BigDecimal): PaymentResult
}
// 2. Concrete Strategies
class CardPaymentStrategy(
private val cardNumber: String,
private val cvv: String
) : PaymentStrategy {
override suspend fun pay(amount: BigDecimal): PaymentResult {
// API bancária via Retrofit
return PaymentResult(success = true, transactionId = "tx_${UUID.randomUUID()}")
}
}
class PayPalPaymentStrategy(
private val email: String
) : PaymentStrategy {
override suspend fun pay(amount: BigDecimal): PaymentResult {
// Integração PayPal SDK
return PaymentResult(success = true, transactionId = "pp_${UUID.randomUUID()}")
}
}
// 3. Context
class PaymentProcessor(
private val strategy: PaymentStrategy
) {
fun setStrategy(strategy: PaymentStrategy): PaymentProcessor {
return PaymentProcessor(strategy)
}
suspend fun processPayment(amount: BigDecimal): PaymentResult {
return strategy.pay(amount)
}
}
// Uso no ViewModel
class CheckoutViewModel : ViewModel() {
private var processor = PaymentProcessor(CardPaymentStrategy("4111...", "123"))
fun payWithCard() {
viewModelScope.launch {
val result = processor.processPayment(BigDecimal("99.99"))
// Processamento do resultado
}
}
}
Strategy com Hilt/Dagger — em projetos Android, as estratégias são frequentemente injetadas via DI. Hilt fornece uma implementação concreta de PaymentStrategy através de @Binds ou @Provides. Isso permite alterar a estratégia sem modificar o código do contexto — basta alterar o módulo DI para outra compilação (debug/release). Por exemplo, MockPaymentStrategy é injetado para depuração, uma estratégia bancária real para produção. A combinação Strategy + DI oferece máxima flexibilidade.
Strategy vs State — estruturalmente os padrões são idênticos: ambos usam composição com uma interface e classes concretas. A diferença está no propósito: Strategy seleciona um algoritmo independente, State controla o comportamento do objeto dependendo de seu estado. No State, o próprio contexto muda a estratégia quando o estado muda; no Strategy, o contexto não controla a troca — o cliente define explicitamente o algoritmo. As estratégias não sabem umas das outras, enquanto os estados podem fazer transições entre si.
Strategy vs Command — Command encapsula uma única ação como objeto, Strategy encapsula um conjunto de algoritmos intercambiáveis. Command é "o que fazer" (uma única chamada execute), Strategy é "como fazer" (um algoritmo de várias etapas). Command é usado para filas, execução adiada, desfazer/refazer. Strategy é usado para escolher como realizar uma tarefa em tempo de execução. Comandos podem ser parametrizados com estratégias, combinando ambos os padrões.
| Característica | Strategy | State | Command | Template Method |
|---|---|---|---|---|
| Propósito | Algoritmos intercambiáveis | Comportamento por estado | Encapsulamento de solicitação | Esqueleto de algoritmo |
| Troca | Explicitamente pelo cliente | Automaticamente pelo contexto | Pelo cliente ou fila | Por herança |
| Nível | Objeto (composição) | Objeto (composição) | Objeto | Classe (herança) |
Strategy vs Template Method — ambos os padrões definem algoritmos, mas de maneiras diferentes. Template Method usa herança: uma classe base define o esqueleto do algoritmo (método template), as subclasses sobrescrevem etapas individuais. Strategy usa composição: o algoritmo é totalmente externalizado para uma classe separada. Template Method é mais simples para casos com estrutura de algoritmo fixa, Strategy — quando os algoritmos são completamente diferentes e podem mudar dinamicamente.
Processamento de pagamentos — o exemplo clássico do Strategy. O carrinho de compras de uma loja online contém uma lista de itens, e o método de pagamento é escolhido pelo usuário. Cada método (cartão, PayPal, Apple Pay, Google Pay, criptomoeda) é uma estratégia separada com uma assinatura pay(amount) comum. O contexto PaymentProcessor não sabe como exatamente o pagamento é processado — ele chama o método comum. Adicionar um novo método de pagamento não requer alterar o código do carrinho.
Validação de dados — Strategy é usado para diferentes regras de validação do mesmo campo. EmailValidatorStrategy, PhoneValidatorStrategy, AgeValidatorStrategy implementam uma interface comum ValidationStrategy com o método validate(input). Um formulário de registro usa um conjunto de estratégias para verificar cada campo. As estratégias de validação podem ser combinadas em uma cadeia (Chain of Responsibility) ou aplicadas todas de uma vez em um loop. Isso substitui longas verificações if-else por uma coleção de validadores polimórficos.
// Estratégia de ordenação
protocol SortingStrategy {
func sort<T>(_ items: [T]) -> [T] where T: Comparable
}
struct QuickSortStrategy: SortingStrategy {
func sort<T>(_ items: [T]) -> [T] { /* quicksort */ items }
}
struct MergeSortStrategy: SortingStrategy {
func sort<T>(_ items: [T]) -> [T] { /* mergesort */ items }
}
class SortedDataSource<T> {
private var strategy: SortingStrategy
func display(_ items: [T]) { let sorted = strategy.sort(items) }
}
Autenticação — em aplicativos móveis, as estratégias de autenticação são trocadas dependendo do provedor. AuthStrategy com métodos login(), logout(), getToken() é implementado para EmailPasswordAuth, GoogleAuth, AppleAuth, BiometricAuth. O contexto AuthManager aceita a estratégia via DI ou factory. Isso permite adicionar novos provedores de autenticação sem alterar a tela de login. O padrão Strategy é a base para muitas bibliotecas OAuth e Firebase Authentication.
Perguntas frequentes
Strategy é justificado quando você tem 3+ algoritmos que podem mudar ou se expandir. Se há 2 algoritmos e eles são estáveis — um simples if-else é menos custoso. Use Strategy quando os algoritmos são usados em diferentes partes do aplicativo, quando precisa trocar algoritmos em tempo de execução, ou quando cada algoritmo requer suas próprias dependências e testes.
Não, são padrões diferentes com estrutura semelhante. Strategy — o cliente seleciona explicitamente um algoritmo, e as estratégias são independentes. State — o objeto em si muda seu comportamento quando seu estado interno muda, e os estados podem fazer transições entre si. No State, o contexto gerencia as mudanças de estado; no Strategy, o código do cliente faz isso.
Sim, em Swift e Kotlin uma estratégia pode ser passada como closure ou lambda. Swift: typealias PaymentHandler = (Decimal) async throws -> PaymentResult. Kotlin: typealias PaymentFun = suspend (BigDecimal) -> PaymentResult. Isso simplifica o código para casos simples, mas perde nomenclatura e documentação. Para 1-2 algoritmos, um closure é suficiente; para 4+, é melhor usar classes separadas.
Cada estratégia é testada com um teste unitário separado usando dependências mock. O Context é testado com uma estratégia mock — verificando se o contexto chama o método da estratégia e passa os parâmetros corretos. Em Swift, use XCTest + protocolos para mocks; em Kotlin, use MockK ou Mockito. A principal vantagem: cada estratégia é testada isoladamente sem configuração complexa.
Sim, Strategy é um dos 23 padrões descritos no livro "Design Patterns: Elements of Reusable Object-Oriented Software" (Gamma, Helm, Johnson, Vlissides, 1994). Pertence ao grupo de padrões comportamentais. Pseudônimos: Policy. O código de exemplo original em Smalltalk-80 está disponível na edição original do GoF.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também