From GoF to Lambdas – The Command Pattern

Last year, Mario Fusco wrote a blog post series questioning the way Java developers are implementing the Gang of Four (GoF) patterns. He shared a functional approach, which is easier to read and maintain.

I will do my best to provide a C# version of Mario’s recommendations expanding some examples. Let’s start with the Command pattern.

The Command Pattern – how it is commonly adopted today

The Command Pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.

In Java, this is commonly implemented defining an interface:

interface Command {
    void run();
}

At this point, the developer can provide different command implementations.

public class Logger implements Command {
    public final String message;
 
    public Logger( String message ) {
        this.message = message;
    }
 
    @Override
    public void run() {
        // ..
    }
}

The “consumer” does not have to know about the details of the command execution. Some implementations will just affect the application state, other could save data on disk, for example.

public class FileSaver implements Command {
    public final String message;
 
    public FileSaver( String message ) {
        this.message = message;
    }
 
    @Override
    public void run() {
        // ..
    }
}

In some cases, commands could be used to coordinate work with another components/applications.

public class Mailer implements Command {
    public final String message;
 
    public Mailer( String message ) {
        this.message = message;
    }
 
    @Override
    public void run() {
        // ..
    }
}

It is a good practice to have something to coordinate the execution of one or more instances of this commands.

public class Executor {
    public void execute(List<Command> tasks) {
        for (Command task : tasks) {
            task.run();
        }
    }
}

Finally it is possible to create a list of the commands we want to run

List<Command> tasks = new ArrayList<>();
tasks.add(new Logger( "Hi" ));
tasks.add(new FileSaver( "Cheers" ));
tasks.add(new Mailer( "Bye" ));
 
new Executor().execute( tasks );

Commands, implemented in this way, are just functions wrapped into objects.

The Command Pattern – Mario’s recommendation

The FunctionalInterface concept could make the Command pattern implementation a lot less verbose.

@FunctionalInterface
interface Command {
    void run();
}

If you are not familiar with the @FunctionalInterface annotation, they are used with Single Abstract Method interfaces – like Command. Functional interfaces can be represented with a simple lambda expression or a method reference. Java already provides a functional interface with a run method named Runnable

More natural than creating a class wrapping a single function is to write just that single function, right?

public static void log(String message) {
    // ..
}
 
public static void save(String message) {
    // ..
}
 
public static void send(String message) {
    // ..
}

We don’t need an Executor class as well:

public static void execute(List<Runnable> tasks ) {
    tasks.forEach( Runnable::run );
}

And all the commands could be executed just as before.

List<Runnable> tasks = new ArrayList<>();
tasks.add(() -> log("Hi"));
tasks.add(() -> save("Cheers"));
tasks.add(() -> send("Bye"));
 
execute( tasks );

Mario explains:

Here the Java compiler automatically translates the lambdas with no arguments and invoking the void static methods executing the actions formerly wrapped in commands into anonymous implementation of the Runnable interface thus allowing to collect them in a List of Runnables.

Mario’s recommendation translated to C#

Now, it is time to apply Mario’s recommendation using C#. Let’s go!

First of all, we don’t need to create an interface. We don’t need to check if the BCL provides an interface neither. We have delegates!

public delegate void Command();

Here we are defining a delegate, but we could use Action with no prejudice.

We can use simple methods just like Mario did.

public static void Log(string message)
{
    // ..
}

public static void Save(string message)
{
    // ..
}

public static void Send(string message)
{
    // ..
}

We can also execute it easily:

var commands = new List<Command>
{
    () => Log("Hi"),
    () => Save("Cheers"),
    () => Send("Bye")
};

commands.ForEach(command => command());

In a more “functional” approach, we could convert are functions into HOF.

public static Command Log(string message)
    => () => { /* .. */ };

public static Command Save(string message)
    => () => { /* .. */ };

public static Command Send(string message)
    => () => { /* .. */ };

This could be a good idea, especially if we want to perform some complex setup. Anyway, it removes the need of using lambdas when creating a command list.

var commands = new List<Command>
{
    Log("Hi"),
    Save("Cheers"),
    Send("Bye")
};

commands.ForEach(command => command());

We don’t need to create a class to implement the command pattern. There are simpler alternatives in Java *and* in C#.

Next time, let’s talk about the strategy pattern.

Compartilhe este insight:

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:

Felicidade é uma coisa ótima. Tanto que, geralmente, nos sentimos plenos, inclusive, quando fazemos alguém feliz – não necessariamente a...
Are you designing Microservices? So, I would like to share a fascinating slide deck that I discovered recently. That comes...
Mais preocupante que o posicionamento do presidente é sua falta de constância. Há dois dias, uma medida provisória proposta pelo...
As an experienced .NET developer, you have to deal with NullReferenceException occurrences every day. Am I right? I firmly believe...
Compete ao arquiteto corporativo a responsabilidade de descrever, analisar e comunicar os diversos aspectos da arquitetura corporativa, bem como registrar...
Em todos esses anos tenho recebido relatos de desenvolvedores projetando sistemas com belas arquiteturas. Muitos deles tem levantado um bocado de questões...
Oferta de pré-venda!

Mentoria em
Arquitetura de Software

Práticas, padrões & técnicas para Arquitetura de Software, de maneira efetiva, com base em cenários reais para profissionais envolvidos no projeto e implantação de software.

× Precisa de ajuda?