Padrão Strategy

André MartinsAndré Martins
3 min read

Imagine que você quer executar operações de diferentes maneiras, mas com o mesmo propósito! Talvez um bom exemplo seja a execução de uma função para diferentes tipos de clientes, ou talvez ter que lidar com vários adquirentes para o mesmo endpoint, pegando o que está ativo para lidar de forma diferente. Vamos seguir com o exemplo do cliente, será mais fácil de entender.

enum ClientType
{
    PF,
    PJ,
    FORNECEDOR,
}
class MensagensService {

    public async void EnviarMensagens(Cliente cliente) {

        if (ClienteHelper.TypeCliente(cliente) == ClientType.PF) {
            // lógica para executar o envio da mensagem para cliente pessoa fisica
        }

        if (ClienteHelper.TypeCliente(cliente) == ClientType.PJ) {
            // lógica para executar o envio da mensagem para cliente PJ
        }

        if (ClienteHelper.TypeCliente(cliente) == ClientType.FORNECEDOR) {
            // lógica para executar o envio da mensagem para cliente fornecedor
        }

    }
}

O código acima é muito ruim. Com o passar do tempo, ele se torna ineficiente e confuso. Cada tipo de cliente tem estratégias diferentes para o envio de mensagens, e essas, neste momento, não importam muito. O que importa é que isso tem que ser tratado de forma separada. Existem outras formas de resolver isso, mas hoje estamos falando do strategy.

Solução

Uma boa solução seria uma classe que consegue navegar entre cada estratégia, e aqui vamos definir que cada estratégia se trata de cada tipo de envio de mensagem. Vamos chamar essa classe de contexto.

Também precisamos de uma rota (interface) entre a classe de contexto e as estratégias.

interface Strategy {
    void Execute();
}

class EnvioMensagemPF : Strategy {
    public void Execute() {
        // lógica para executar o envio da mensagem para cliente pessoa fisica
    }
}

class EnvioMensagemPJ : Strategy {
    public void Execute() {
        // lógica para executar o envio da mensagem para cliente PJ
    }
}

class EnvioMensagemFornecedor : Strategy {
    public void Execute() {
        // lógica para executar o envio da mensagem para cliente FORNECEDOR
    }
}

class Context {
    private Strategy Strategy {get; set;}

     public Context(Strategy strategy) {
        Strategy = strategy;
     }

    public void Execute() {
        Console.WriteLine("executando strategy");
        Strategy.Execute();
    }

    public void SetStrategy(Strategy strategy) {
        Strategy = strategy;
    }
}

A partir de agora, ficou bem mais tranquilo adicionar lógica e comportamento de forma separada para cada método Execute de cada tipo de envio.

Factory

Vamos definir uma factory para não precisar lidar com esses ifs de forma excessiva.

class EnvioMensagemFactory
{
    private readonly Dictionary<ClientType, IStrategy> _strategies;

    public EnvioMensagemFactory()
    {
        _strategies = new Dictionary<ClientType, IStrategy>
        {
            { ClientType.PF, new EnvioMensagemPF() },
            { ClientType.PJ, new EnvioMensagemPJ() },
            { ClientType.FORNECEDOR, new EnvioMensagemFornecedor() }
        };
    }

    public IEnvioMensagemStrategy GetStrategy(ClientType tipoCliente)
    {
        if (_strategies.TryGetValue(tipoCliente, out var strategy))
            return strategy;

        throw new Exception("Tipo de cliente não suportado");
    }
}

Executando strategy

class MensagensService
{
    private readonly EnvioMensagemFactory _factory;

    public MensagensService()
    {
        _factory = new EnvioMensagemFactory();
    }

    public async Task EnviarMensagens(Cliente cliente)
    {
        var tipoCliente = ClienteHelper.TypeCliente(cliente);
        var strategy = _factory.GetStrategy(tipoCliente);

        await strategy.EnviarMensagem(cliente);
    }
}

Gostou do resultado? Esse tipo de implementação melhora muito a leitura e a manutenibilidade do código.

0
Subscribe to my newsletter

Read articles from André Martins directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

André Martins
André Martins