Design Pattern - Strategy
Tags: dotnet, csharp, braziliandevs, pattern
Published: 2021-04-28 11:36:38

Vamos falar hoje de um pattern bem interessante quando trabalhamos com programação orientada a objetos, que pode ser aplicado em muitos casos do nosso dia a dia. Durante esse artigo vamos conhecer a definição do pattern, ver um exemplo de código que frequentemente nos deparamos no dia a dia e como podemos melhorá-lo aplicando o pattern.

Os exemplos desse artigo serão em C# porém o pattern é facilmente aplicado em outras linguagens.

## Definição
Segundo Erich Gamma em seu livro sobre Design Patterns, a definição para o pattern strategy é:
>Definir uma família de algoritmos, encapsular cada uma delas e torná-las intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam.

## Implementando um exemplo para pagamentos
No nosso exemplo iremos criar um código para efetuar pagamento, seja ele por cartão de crédito, débito e outras modalidades. Para isso então vamos criar suas interfaces.

```csharp
public interface IPagamentoCartaoCreditoService
{
	string EfetuarPagamento(decimal valor);
}

public interface IPagamentoCartaoDebitoService
{
	string EfetuarPagamento(decimal valor);
}
```

Criamos uma interface para cada forma de pagamento que possui declarado o método EfetuarPagamento.

Vamos agora implementar as classes concretas das interfaces.
```csharp
public class PagamentoCartaoCreditoService 
	: IPagamentoCartaoCreditoService
{
	public string EfetuarPagamento(decimal valor) 
		=> $"Pagamento efetuado com o cartão de crédito no valor de R${valor}";
}

public class PagamentoCartaoDebitoService 
	: IPagamentoCartaoDebitoService
{
	public string EfetuarPagamento(decimal valor) 
		=> $"Pagamento efetuado com o cartão de débito no valor de R${valor}";
}
```

E por fim vamos implementar nossa regra de pagamento.

```csharp
public class PagamentoService
{
	private readonly IPagamentoCartaoCreditoService _cartaoCreditoService;
	private readonly IPagamentoCartaoDebitoService _cartaoDebitoService;

	public PagamentoService(IPagamentoCartaoCreditoService cartaoCreditoService,
		IPagamentoCartaoDebitoService cartaoDebitoService)
	{
		_cartaoCreditoService = cartaoCreditoService;
		_cartaoDebitoService = cartaoDebitoService;
	}

	public string EfetuarPagamento(FormaPagamentoEnum formaPagamento, decimal valor)
	{
		if (formaPagamento == FormaPagamentoEnum.CartaoCredito)
			return _cartaoCreditoService.EfetuarPagamento(valor);
		else if (formaPagamento == FormaPagamentoEnum.CartaoDebito)
			return _cartaoDebitoService.EfetuarPagamento(valor);
		else
			throw new Exception("Forma de pagamento não encontrada.");
	}
}
```

Implementamos um método para efetuar o pagamento e dependendo do valor passado pelo enum ele efetua o pagamento por cartão de crédito ou por cartão de débito. Acredito que você já tenha visto um código similar a esse, note que não tem nada de errado nesse código, mas agora vamos ver o que precisamos para implementar uma nova forma de pagamento.

1. Implementar a nova forma de pagamento (interface e classe concreta)
2. Modificar o construtor da classe PagamentoService passando a interface da nova forma de pagamento.
3. Criar uma nova condição if para poder ser utilizado essa nova forma de pagamento.

Se atualizarmos esse código com dez novas formas de pagamento (o que não é difícil hoje em dia com tantas opções de pagamento hehehe) teremos um código cada vez maior, complexo e aberto constantemente a modificações (o que vai contra a letra 'O' dos princípios do SOLID).

Então vamos aplicar algumas melhorias nesse código com o nosso pattern strategy.

##Implementando o pattern strategy

Vamos começar criando apenas uma interface de pagamento de forma genérica.

```csharp
public interface IPagamentoService
{
	FormaPagamentoEnum formaPagamento { get; }
	string EfetuarPagamento(decimal valor);
}
```

Perceba que agora definimos o enum dentro da interface também. As classes concretas são implementadas à partir desta interface.

```csharp
public class PagamentoCartaoCreditoService 
	: IPagamentoService
{
	public FormaPagamentoEnum formaPagamento 
		=> FormaPagamentoEnum.CartaoCredito;

	public string EfetuarPagamento(decimal valor) 
		=> $"Pagamento efetuado com o cartão de crédito no valor de R${valor}";
}

public class PagamentoCartaoDebitoService 
	: IPagamentoService
{
	public FormaPagamentoEnum formaPagamento 
		=> FormaPagamentoEnum.CartaoDebito;

	public string EfetuarPagamento(decimal valor) 
		=> $"Pagamento efetuado com o cartão de débito no valor de R${valor}";
}
```

E agora vem a parte mais interessante que é a implementação do pattern.

```csharp
public class PagamentoStrategyService 
	: IPagamentoStrategyService
{
	private readonly IEnumerable<IPagamentoService> _services;

	public PagamentoStrategyService(IEnumerable<IPagamentoService> services)
	{
		_services = services;
	}

	public string EfetuarPagamento(FormaPagamentoEnum formaPagamento, decimal valor)
	{
		return _services.FirstOrDefault(x => x.formaPagamento == formaPagamento)?
			.EfetuarPagamento(valor) ?? 
			throw new ArgumentNullException(nameof(formaPagamento));
	}
}
```

Como temos duas classes que implementam a mesma interface `IPagamentoService`, recebemos no construtor um IEnumerable contendo as duas implementações. No método de efetuar pagamento criamos uma expressão lambda filtrando qual implementação da interface `IPagamentoService` iremos utilizar à partir do valor do enum.

Muito simples não? Você não vai mais precisar de vários `if else`, a classe estará pronta para receber quantas formas de pagamento forem necessárias sem precisar modificá-la. Toda vez que precisarmos implementar uma nova forma de pagamento o único passo será:

1. Implementar a nova forma de pagamento (classe concreta utilizando a interface `IPagamentoService`)

## Projeto de exemplo
Caso tenha ficado ainda alguma dúvida no código, você pode ver o exemplo que eu criei nesse [repositório do Github](https://github.com/marcosbelorio/strategy-pattern-example).

Se gostou utilize os botões do lado esquerdo para que eu possa saber. Caso tenha alguma dúvida, sugestão ou crítica, deixe um comentário logo abaixo.

Muito obrigado!

Referências:
[What You Need To Know About The Helpful Strategy Pattern](https://dev.to/kylegalbraith/what-you-need-to-know-about-the-helpful-strategy-pattern-34pm)
[WestDiscGolf/StrategyPatternExample](https://github.com/WestDiscGolf/StrategyPatternExample)