From GoF to Lambdas – The Strategy Pattern

In the previous post, I mentioned Mario Fusco who wrote a blog post series questioning the way Java developers are implementing the Gang of Four (GoF) patterns.

I am trying to provide a C# version of Mario’s recommendations expanding some examples. In this post, I would like to talk about the Strategy Pattern.

The Strategy Pattern

The goal of this pattern is to define a family of algorithms, encapsulate each one, and make them interchangeable. The algorithm varies independently from clients that use it.

There are three primary participants in this pattern:

  1. The strategy interface, which declares an interface common to all supported algorithms.
  2. The strategy concrete implementation, which implements the algorithm according to the strategy interface.
  3. The execution context, which is configured with a specific implementation and to define an interface that lets the strategy access its data.

How it is commonly adopted today

The first step is putting an abstract definition of the behaviors (algorithms) we want to support into an interface. Let’s assume we need to provide different sorting algorithms. So, the first step would be to define a standard interface with a sort method:

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

Then, we would need to write concrete implementations of the sorting Strategy:

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)
    {
        // ..
    }
}

After that, we would be ready to use it.

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

Mario’s recommendation

The strategy implementation using interfaces is too verbose. Don’t you think? We are defining classes just to wrap functions.

Let’s use a delegate instead of an interface:

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

Now, we can write concrete implementations as simple functions:

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) {  /* .. */ }
}

This is an apparently less coupled solution. We do not have to reference the strategy interface in the concrete implementation. So, we could provide concrete implementations in different assemblies with no dependencies.

In the context, we just need to specify the delegate we need instead of the interface.

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

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

        // ..
    }
}

The dependency injection container provided by ASP.net core supports it:

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

That’s it. Next time, we will talk about the Template pattern.

Compartilhe este insight:

Deixe uma resposta

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:

Um servidor de identidades é um artefato de sofware que centraliza os dados de usuários, bem como o processo para...
NOTA DO ELEMAR: Este post foi escrito por Fernando Neiva de Paiva e editado por mim. Já fui cético com...
Parsing large files is a recurring and challenging task. Right? It is too easy to write slow code that consumes...
Há muitos anos, tinha o hábito de fazer elogios públicos a tudo que achava que estava sendo bem-feito. Achava honestamente...
Eu sei que sou privilegiado, em última instância, por poder oferecer, através do meu trabalho, algo que a sociedade valoriza....
Investimos consideravelmente no planejamento estratégico de nossas organizações. Entretanto, a execução não tem sido tarefa fácil. Em minha interpretação, boa...