Implementação Funcional de Design Patterns – Strategy

No post anterior, eu mencionei uma série de post do Mario Fusco questionando a forma como desenvolvedores Java estão implementando os design patterns propostos pelo GoF.

Estou tentando prover uma versão C# das recomendações de Fusco, expandindo alguns exemplos. Neste post, eu gostaria de falar sobre o padrão Strategy.

Sobre o padrão

O objetivo do padrão strategy é definir uma “família de algoritmos”, encapsulando cada um deles, tornando-os intercambiáveis. A implementação poderia variar independete da forma como o cliente a utilizaria.

Os três elementos fundamentais desse padrão são:

  1. A interface para a estratégia, que declara uma interface comum suportada por todas as implementações.
  2. As implementações concretas, que estão de acordo com o especificado na interface.
  3. O contexto de execução, que é configurado para uma implementação específica e que mantem/fornece as entradas.

 

Como geralmente é implementado

O primeiro passo é colocar a definição dos comportamentos que desejamos suportar em uma interface. Vamos assumir que queremos prover diferentes algoritmos de ordenação, por exemplo. Nesse caso, o primeiro passo seria definir uma interface padrão com um método de ordenação.

public interface IArraySortStrategy
{
    T[] Sort<T>(T[] input, Comparison<T> comparison);
}

Então, podemos escrever as implementações concretas:

public class QuickSortStrategy : IArraySortStrategy
{
    public T[] Sort<T>(T[] input, Comparison<T> comparison)
    {
        // ..
    }
}

public class MergeSortStrategy : IArraySortStrategy
{
    public T[] Sort<T>(T[] input, Comparison<T> comparison)
    {
        // ..
    }
}

public class BubbleSortStrategy : IArraySortStrategy
{
    public T[] Sort<T>(T[] input, Comparison<T> comparison)
    {
        // ..
    }
}

Assim, estamos prontos para consumir isso.

namespace HelloStrategy.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        private readonly IArraySortStrategy _sortingStrategy;
        
        public ValuesController(IArraySortStrategy sortingStrategy)
        {
            _sortingStrategy = sortingStrategy;
        }
        // ..
    }
}

Recomendação de Fusco

A implementação da estratégia usando interfaces é muito verbosa. Não acha? Afinal, estamos apenas definindo classes para “envolver” funções.

Vamos usar um delegate no lugar de uma interface:

public delegate T[] SortingAlgorithm<T>(T[] input, Comparison<T> comparison);

Agora, podemos escrever implementações concretas como funções simples:

public static class SortingImplementations
{
    public static T[] QuickSort<T>(T[] input, Comparison<T> comparison) {  /* .. */ }
    public static T[] MergeSort<T>(T[] input, Comparison<T> comparison) {  /* .. */ }
    public static T[] BubbleSort<T>(T[] input, Comparison<T> comparison) {  /* .. */ }
}

Esta é uma solução aparentemente menos acoplada. Não precisamos adicionar referências para uma interface. Dessa forma, podemos prover implementações concretas em diferentes assemblies sem que sejam necessárias adições de referências.

No contexto, precisamos apenas especificar que esperamos um delegate no lugar de uma implementação de interface.

namespace HelloStrategy.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        private readonly SortingAlgorithm<Customer> _sortingAlgorithm;

        public ValuesController(SortingAlgorithm<Customer> sortingAlgorithm)
        {
            _sortingAlgorithm = sortingAlgorithm;
        }

        // ..
    }
}

O container de injeção de dependências do ASP.net core suporta isso:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton>(SortingImplementations.QuickSort);
    // Add framework services.
    services.AddMvc();
}

Era isso. Na próxima vez, vamos falar sobre o padrão Template.

Compartilhe este insight:

Uma resposta

  1. Caso você desenvolva outra implementação de ordenação, teria que adicionar um outro método à classe `SortingImplementations`. Isso quebraria o Open Closed Principle, não?

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mais insights para o seu negócio

Veja mais alguns estudos e reflexões que podem gerar alguns insights para o seu negócio:

When designing systems that need to scale you always need to remember that [tweet]more computing power does not necessarily mean...
Conheci o poema maravilhoso da Viviane Mosé, transcrito abaixo, na interpretação de uma grande amiga. Quem tem olhos pra ver...
Entropia é um conceito oriundo da física que trata da quantidade de “desordem” em um sistema. As leis da termodinâmica...
Retomar em plenitude todas as atividades econômicas, aumentando o risco de contágio pelo novo coronavírus e, eventualmente, sacrificar uma parcela...
Criar uma visualização para o fluxo de trabalho é uma das primeiras recomendações do Kanban e tem uma série de...
The following works as expected when building in Debug – the execution is done after three seconds. But, for some...