# Padrão Strategy

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.

```csharp
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.

```csharp
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.

```csharp
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

```csharp
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.
