Padrão Strategy
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 55 de 70.
O padrão Strategy é um padrão comportamental que permite definir uma família de algoritmos, encapsular cada um em sua própria classe e torná-los intercambiáveis em tempo de execução. Em vez de codificar o comportamento de forma rígida, você injeta a estratégia desejada em um objeto.
O padrão consiste em três partes: uma interface de estratégia que define o contrato do algoritmo, classes de estratégia concretas que implementam diferentes comportamentos e uma classe de contexto que usa uma estratégia:
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via PayPal");
}
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentMethod(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal total)
{
_paymentStrategy.Pay(total);
}
}O contexto (ShoppingCart) não sabe qual método de pagamento está usando - ele apenas chama Pay() em qualquer estratégia que tenha sido fornecida:
var cart = new ShoppingCart();
cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m); // Pago 99.99 via Cartão de Crédito
cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m); // Pago 49.99 via PayPalO padrão Strategy elimina a lógica condicional complexa. Adicionar um novo método de pagamento significa criar uma nova classe que implementa IPaymentStrategy - nenhuma alteração é necessária em ShoppingCart. Isso torna seu código aberto para extensão, mas fechado para modificação.
Desafio
FácilVamos construir uma calculadora de custos de envio usando o padrão Strategy. Diferentes métodos de envio possuem diferentes algoritmos de precificação, e seu sistema deve permitir a alternância entre eles em tempo de execução sem alterar a lógica central de processamento de pedidos.
Você organizará seu código em três arquivos:
ShippingStrategy.cs: Defina uma interfaceIShippingStrategyno namespaceShippingcom um métodoCalculateCost(double weight)que retorna umdouble. Em seguida, crie três classes de estratégia concretas:StandardShipping- calcula o custo comoweight * 1.5ExpressShipping- calcula o custo comoweight * 3.0 + 5.0(inclui uma taxa fixa expressa)OvernightShipping- calcula o custo comoweight * 5.0 + 10.0(serviço premium com taxa base mais alta)
OrderProcessor.cs: Crie uma classeOrderProcessorno mesmo namespace. Esta é a sua classe de contexto que utiliza uma estratégia de envio. Ela deve ter:- Um campo privado para armazenar a
IShippingStrategyatual - Um método
SetShippingMethod(IShippingStrategy strategy)para alterar a estratégia de envio - Um método
ProcessOrder(double weight)que utiliza a estratégia atual para calcular e retornar o custo de envio
- Um campo privado para armazenar a
Program.cs: Reúna tudo criando umOrderProcessore demonstrando como você pode trocar as estratégias de envio em tempo de execução. O mesmo processador pode calcular custos usando diferentes algoritmos simplesmente alterando sua estratégia.
Você receberá duas entradas:
- O tipo de método de envio:
standard,express, ouovernight - O peso do pacote como um número decimal (ex:
4.5)
Crie um OrderProcessor, defina a estratégia de envio apropriada com base no tipo de entrada, processe o pedido e imprima o custo calculado.
Por exemplo, se as entradas forem express e 4.5, a saída deve ser:
18.5Isso demonstra o poder do padrão Strategy - seu OrderProcessor não contém nenhuma lógica de cálculo de envio por si só. Ele simplesmente delega para qualquer estratégia que lhe tenha sido fornecida, facilitando a adição de novos métodos de envio (como DroneDelivery) sem modificar o processador!
Folha de consulta
O padrão Strategy é um padrão comportamental que define uma família de algoritmos, encapsula cada um em sua própria classe e os torna intercambiáveis em tempo de execução.
O padrão consiste em três partes:
- Interface Strategy: define o contrato do algoritmo
- Classes de estratégia concreta: implementam diferentes comportamentos
- Classe de contexto: usa uma estratégia sem conhecer seus detalhes de implementação
Estrutura de exemplo:
// Interface Strategy
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Estratégias concretas
public class CreditCardPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via Credit Card");
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay(decimal amount) =>
Console.WriteLine($"Paid {amount} via PayPal");
}
// Classe de contexto
public class ShoppingCart
{
private IPaymentStrategy _paymentStrategy;
public void SetPaymentMethod(IPaymentStrategy strategy)
{
_paymentStrategy = strategy;
}
public void Checkout(decimal total)
{
_paymentStrategy.Pay(total);
}
}Uso - alternando estratégias em tempo de execução:
var cart = new ShoppingCart();
cart.SetPaymentMethod(new CreditCardPayment());
cart.Checkout(99.99m); // Paid 99.99 via Credit Card
cart.SetPaymentMethod(new PayPalPayment());
cart.Checkout(49.99m); // Paid 49.99 via PayPalBenefícios:
- Elimina lógica condicional complexa
- Aberto para extensão, fechado para modificação
- Adicionar novas estratégias requer apenas a criação de novas classes, sem alterações no código existente
Experimente você mesmo
using System;
using Shipping;
class Program
{
public static void Main(string[] args)
{
// Ler entrada
string shippingType = Console.ReadLine();
double weight = Convert.ToDouble(Console.ReadLine());
// TODO: Create an OrderProcessor instance
// TODO: Based on shippingType ("standard", "express", or "overnight"),
// crie a estratégia de envio apropriada e defina-a no processor
// TODO: Process the order and print the calculated cost
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos