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 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:

In this post, I’m going to share with you one of the RavenDB 4 features that I like the most:...
Pimenta nos olhos dos outros é refresco. Quando não é pela satisfação sádica proporcionada pela dor alheia é pelo alívio...
Muitas vezes, em nossos sistemas, temos tarefas que demandam processamento de  longa duração ou possuem alta complexidade computacional. Na medida...
No meu tempo absolutamente livre – quando não estou trabalhando, estudando ou aproveitando com a família – tenho jogado Mario...
A aquisição de novos clientes é importante para o crescimento. Entretanto, a manutenção dos clientes atuais tende a ser mais...
Na vida, todos temos “momentos traumáticos”. Ou seja, aqueles momentos que, de repente, mudam tudo e, ao mesmo tempo, de...
× Precisa de ajuda?