Strategy Design Pattern
Tags: designpatterns, csharp, programming, solidprinciples
Published: 2023-08-20 17:24:12


Tá, antes de tudo, você sabe o que são design patterns? Design patterns são soluções reutilizáveis e já bem estabelecidas para problemas comuns que nós, programadores, enfrentamos no dia a dia. 

Surgidos através do trabalho de diversos programadores experientes, se tornaram um consenso na resolução de problemas, a fim de tornar o seu código mais limpo, elegante e de fácil manutenção. Talvez você entenda melhor conforme vá realizando essa leitura.

## O problema a se resolver

Já que estamos falando de uma solução, estamos resolvendo algum problema certo?

Para demonstrar o problema, vou criar o seguinte contexto:

Vamos considerar um sistema simples para calcular descontos em uma loja online com base em diferentes tipos de cupons existentes: EasySave ou ClearCut, onde cada um tem sua **estratégia** de desconto → o EasySave fornece 20% de desconto e o ClearCut fornece apenas 10%.

O codigo inicial está da seguinte forma:

```C#
public class Order
{
    public double Value { get; set; }
}
```
```C#
public class DiscountCalculator
{
    public void Calculate(Order order, string coupon) 
    {        
        if( "EasySave".Equals(coupon))
        {   
            double value = order.Value * 0.8;
            Console.WriteLine(value);
        } 
        else if( "ClearCut".Equals(coupon)) 
        {    
            double value = order.Value * 0.9;
            Console.WriteLine(value);
        }
    }
}
```

Existem alguns problemas nessa implementação:

Se formos analisar, para cada novo desconto criado, será necessário criar um novo `if`, podendo se tornar um método muito grande e difícil de se ler e consequentemente, de se manter. Outra questão, é que essa classe está pouco coesa, as regras de negócio e cálculos estão espalhadas por ela.

## Algumas melhorias

Para resolver o problema da coesão, podemos separar a responsabilidade do cálculo dos descontos para classes específicas de cada um, dessa forma:

```C#
public class EasySave
{
    public double Calculate(Order order)
    {
        return order.Value * 0.8;
    }
}
```
```C#
public class ClearCut
{
    public double Calculate(Order order)
    {
        return order.Value * 0.9;
    }
}
```
```C#
public class DiscountCalculator
{
    public void Calculate(Order order, string coupon)
    {
        if ("EasySave".Equals(coupon))
        {
            double value = new EasySave().Calculate(order);
            Console.WriteLine(value);
        }
        else if ("ClearCut".Equals(coupon))
        {
            double value = new ClearCut().Calculate(order);
            Console.WriteLine(value);
        }
    }
}

```

Pronto, resolvemos o problema de coesão agora que cada classe de desconto é responsável por suas regras. No entanto, ainda temos o problema da quantidade de `if` que esse bloco de código pode ter para cada desconto novo adicionado.

Para remover as condicionais desse método, temos que remover o parâmetro `coupoun` que informa, através de uma `string`, o desconto que será utilizado, concorda?

Tá, mas como saberemos qual desconto será aplicado? Uma solução, seria criar um método separado para cada desconto, dessa maneira:

```C#
public class DiscountCalculator
{
    public void CalculateEasySave(Order order)
    {
        double value = new EasySave().Calculate(order);
        Console.WriteLine(value);
    }
    public void CalculateClearCut(Order order)
    {
        double value = new ClearCut().Calculate(order);
        Console.WriteLine(value);
    }
}
```
Agora ficou melhor, mas ainda não resolve o problema, já que agora, ao invés de ter um novo `if`, ainda teremos que criar um novo método para cada imposto. Então, como resolver?

## A solução final

Como você provavelmente já percebeu, os dois métodos que criamos na classe `DiscountCalculator` implementam uma **estratégia** específica para o seu desconto, mas no final das contas, não será sempre sobre um desconto e, nesse caso o cálculo em cima dele?

Isso significa que podemos criar uma abstração de desconto, e definir um método genérico que deva calcular o valor do pedido com o desconto!

Faremos essa abstração da seguinte maneira, utilizando uma interface:

```C#
public interface Discount
{
    public double Calculate(Order order);
}
```
Com a interface criada, devemos agora fazer com que as classes `EasySave` e `ClearCut` herdem da interface `Discount`:

```C#
public class EasySave : Discount
{
    public double Calculate(Order order)
    {
        return order.Value * 0.8;
    }
}
```
```C#
public class ClearCut : Discount
{
    public double Calculate(Order order)
    {
        return order.Value * 0.9;
    }
}
```

Para finalizar, na classe `DiscountCalculator`, ao invés de utilizar um método para cada desconto existente na nossa aplicação, utilizaremos só um método que receberá como parâmetro, além do pedido, o desconto a ser calculado, assim:

```C#
public class DiscountCalculator
{
    public void Calculate(Order order, Discount discount)
    {
        double value = discount.Calculate(order);
        Console.WriteLine(value);
    }
}
```

Pronto! Agora, a criação de novos tipos de desconto não implica mais numa mudança da classe `DiscountCalculator`, apenas na criação de uma nova classe que implemente a interface `Discount` . Com isso, temos um código mais limpo e mais fácil de se realizar manutenção.

[Guia do Refactoring Guru sobre o padrão Strategy](https://refactoring.guru/pt-br/design-patterns/strategy)

Aaah, lembre-se sempre que um design pattern não é sobre seguir a implementação à risca, é sobre seguir a ideia.