Design Patterns: Strategy
Tags: csharp, strategy, tutorial, designpattern
Published: 2022-03-12 01:21:33

**O Strategy** é um **padrão de projeto comportamental** que permite que você defina uma família de algoritmos, coloque-os em classes separadas, e faça os objetos deles serem intercambiáveis.

### Características:

* Context
    * Tem a referencia para estratégia que sera utilizada.
* IStrategy
    * Define a interface para a estratégia dada.
* Strategy
    * Implementação concentra para a estratégia.

### Exemplos teórico

#### Cenário 1: Calcular as taxas para diferentes países:

* O programa irá executar "IStrategy.GetTax(order)" [Context=order]
* O contrato(interface) define: "GetTaxFor(Order oder)" [IStrategy]
* Estratégicas: "SwedenSalesTaxStrategy, USAStateSalesTaxStrategy" [Strategy]

#### Cenário 2: Criar uma fatura:

* O programa irá executar "IStrategy.CreateInvoice(order)" [Context=order]
* O contrato(interface) define: "CreateInvoice(Order oder)" [IStrategy]
* Estratégicas: "PDFInvoiceStrategy, EmailInvoiceStrategy, PrintInvoiceStrategy" [Strategy]

#### Cenário 3: Enviar notificação:

* O programa irá executar "IStrategy.SendNotification(notification)" [Contexto=notification]
* O contrato(interface) define: "SendNotification(Notification notification)" [IStrategy]
* Estratégicas: "EmailNotificationStrategy, SmsNotificationStrategy, PushNotificationStrategy" [Strategy]


### Resumo:

Com o Design Pattern Strategy, podemos selecionar uma implementação em tempo de execução(runtime) com base em uma abstração(interface), como por exemplo a entrada do usuário, sem precisar estender a classe.

O nosso contexto, por exemplo order citado no cenário 1 e 2, ou notification citado no cenário 3,  tem referencia para a interface da estratégia de como calculator a taxa e enviar a fatura no cenário 1 e 2 respectivamente, ou enviar uma notificação no cenário 3. O contexto não precisa saber como a estratégia será resolvida, e sim executa-la.


### Show me the code:

Vamos utilizar o cenário 3 para exemplificar o padrão Strategy, o código fonte pode ser encontrado nesse repositório: [https://github.com/paulowalravendev/article-design-pattern-strategy](https://github.com/paulowalravendev/article-design-pattern-strategy)

Imaginemos que voce está implementando uma feature que será responsável por mandar notificações, voce cria as seguintes classes de modelo:

```cs
// NotificationChannels.cs
public enum NotificationChannels
{
   Email,
   Sms,
   PushNotification,
}
```

```cs
// UserNotificationSettings.cs
public class UserNotificationSettings
{
   public UserNotificationSettings(NotificationChannels notificationChanel)
   {
       NotificationChanel = notificationChanel;
       EnableNotification = true;
   }

   public NotificationChannels NotificationChanel { get; set; }
   public bool EnableNotification { get; set; }
}
```

```cs
// Notification.cs
public class Notification
{
   public Notification(string content, UserNotificationSettings settings)
   {
       Content = content;
       Settings = settings;
   }

   public string Content { get; set; }
   public UserNotificationSettings Settings { get; set; }

   public void Send()
   {
       if (Settings.EnableNotification)
       {
           switch (Settings.NotificationChanel)
           {
               case NotificationChannels.Email:
                   Console.WriteLine($"Send notification: \"{Content}\" by email");
                   break;
               case NotificationChannels.Sms:
                   Console.WriteLine($"Send notification: \"{Content}\" by sms");
                   break;
               case NotificationChannels.PushNotification:
                   Console.WriteLine($"Send notification: \"{Content}\" by push notification");
                   break;
               default:
                   throw new ArgumentOutOfRangeException();
           }

           return;
       }

       Console.WriteLine("Cannot send notification because the user has disabled");
   }
}
```

```cs
// Program.cs
var notification1 = new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.Email));
var notification2 = new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.Sms));
var notification3 =
   new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.PushNotification));

notification1.Send();
notification2.Send();
notification3.Send();
```

Temos dois grandes problemas nesse código de exemplo acima, a classe Notificatino no método Send está sabendo como envia a notificação para vários canais que o programa tem hoje, o segundo problema é que se amanhã aparecer um novo canal precisaremos modificar o método de Send novamente, se a implementação de envio por e-mail ou qualquer outro canal mudar, teremos que fazer a mesmo coisa, modificar nossa classe Notification.

Violamos dois princípios importantíssimos, o SRP(Principio da responsabilidade única) e o OCP(Principio aberto-fechado).

Utilizaremos o pattern Strategy para resolver isso, já temos nosso contexto [Notification], e agora vamos implementar a abstração da estratégia[void Send()] e suas implementações [EmailNotificationStrategy, SmsNotificationStrategy, PushNotificationStrategy]

```cs
// INotificationStrategy.cs
public interface INotificationStrategy
{
   void Send(Notification notification);
}
```

```cs
// EmailNotificationStrategy.cs
public class EmailNotificationStrategy : INotificationStrategy
{
   public void Send(Notification notification)
   {
       if (notification.Settings.EnableNotification == false)
       {
           Console.WriteLine("Cannot send notification because the user has disabled");
       }

       if (notification.Settings.NotificationChanel != NotificationChannels.Email)
       {
           throw new ApplicationException("Strategy is wrong");
       }

       Console.WriteLine($"Send notification: \"{notification.Content}\" by email");
   }
}
```

```cs
public class PushNotificationStrategy : INotificationStrategy
{
   public void Send(Notification notification)
   {
       if (notification.Settings.EnableNotification == false)
       {
           Console.WriteLine("Cannot send notification because the user has disabled");
       }

       if (notification.Settings.NotificationChanel != NotificationChannels.PushNotification)
       {
           throw new ApplicationException("Strategy is wrong");
       }

       Console.WriteLine($"Send notification: \"{notification.Content}\" by push notification");
   }
}
```



```cs
// SmsNotificationStrategy.cs
public class SmsNotificationStrategy : INotificationStrategy
{
   public void Send(Notification notification)
   {
       if (notification.Settings.EnableNotification == false)
       {
           Console.WriteLine("Cannot send notification because the user has disabled");
       }

       if (notification.Settings.NotificationChanel != NotificationChannels.Sms)
       {
           throw new ApplicationException("Strategy is wrong");
       }

       Console.WriteLine($"Send notification: \"{notification.Content}\" by sms");
   }
}
```

Com a abstração e as implementações das estratégias criadas, vamos modificar o contexto "Notification.cs" para utiliza-las.

```cs
// Notification.cs
public class Notification
{
   public INotificationStrategy? NotificationStrategy { get; set; }

   public Notification(string content, UserNotificationSettings settings)
   {
       Content = content;
       Settings = settings;
   }

   public string Content { get; set; }
   public UserNotificationSettings Settings { get; set; }

   public void Send()
   {
       if (NotificationStrategy == null) throw new ArgumentException("NotificationStrategy is required");
       NotificationStrategy.Send(this);
   }
}
```

Na utilização precisaremos passar a estratégica que iremos utilizar na hora do envio, decidi passar por propriedade, voce também poderia passar por constructor modificando a classe Notification.cs se assim preferir.

```cs
// Program.cs
var notification1 = new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.Email));
var notification2 = new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.Sms));
var notification3 =
   new Notification("Hello Strategy", new UserNotificationSettings(NotificationChannels.PushNotification));

notification1.NotificationStrategy = new EmailNotificationStrategy();
notification2.NotificationStrategy = new SmsNotificationStrategy();
notification3.NotificationStrategy = new PushNotificationStrategy();

notification1.Send();
notification2.Send();
notification3.Send();
```

Se amanha aparecer uma nova maneira de notificar, só precisaremos implementar uma nova estratégia e utilizar.

### Referencia:

[https://refactoring.guru/pt-br/design-patterns/strategy](https://refactoring.guru/pt-br/design-patterns/strategy)

[https://app.pluralsight.com/library/courses/c-sharp-design-patterns-strategy](https://app.pluralsight.com/library/courses/c-sharp-design-patterns-strategy)

Capa - Photo by[ Maarten van den Heuvel](https://unsplash.com/@mvdheuvel?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on[ Unsplash](https://unsplash.com/s/photos/strategy?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)