logo
    • Home
    • Categories
    • About
  • en-languageEnglish
Design patternBy Pierre Colart

Design Pattern Command

Description

The Command design pattern allows the transformation of an action to be executed into an autonomous object containing all the details of the action. Through this transformation, we can:

  • Parameterize methods with different actions.
  • Schedule the execution of methods.
  • Put actions in a queue.
  • Undo performed operations.

Mise en situation

🎮 We are going to develop the functioning of a console:

🕹️ We will have a controller that defines the basic keys. 👾 We will have a game that defines the appropriate movements.

In this case, we have two actors who will interact. One could simply think of creating a button object and simply linking actions to it, by means of a lot of subclasses. The further you go, the more bugs you discover, because the component of your button (key) remembers the previous class, you have subclasses that no longer work, etc.

This is where the Command pattern comes to the rescue:

  • Implementation of a separation of concerns.
  • You have a command interface to implement for each new command.
  • Each game will have its commands that will be executed by the controller. Since every command implements the command, we just have to execute the game method to know the action to perform.

Avantages et inconvénients

Avantages Inconvénients
✔️Single Responsibility: decoupling of processing classes and execution classes. This allows commands to be differentiated. ❌Complexity by adding many layers between the receiver and the command emitter.
✔️Flexibility: adding a new command has no impact on the rest of the code.
✔️Composite command: allows multiple commands to be combined into a more complex command.

Quand l'utiliser ?

💡It is often used on the GUI side. 💡You can use it in the implementation of actions that need to be decoupled.

Diagramme UML

In this example, we are developing a simulation of an online console controller that will take into account the movements of a game. In our case, we will take Zelda as an example.

Implementation

Let's start by creating the base of our design pattern: the Command interface.

 package com.design.command.command;

public interface Command {
    void execute();
}

Our base interface is not very complicated for now, it asks for the implementation of an execute function that will define the action of our controller. Now that we have finished our interface, let's add our receiver which will be the game that will give the commands according to the action.

ZeldaCharacterReceiver

 package com.design.command.character;

public class ZeldaCharacterReceiver {
    private String name;


    public void moveUp() {
        System.out.println(getName() + " Jumping up");
    }

    public void moveDown() {
        System.out.println(getName() + " Jumping down");
    }

    public void moveLeft() {
        System.out.println(getName() + " Moving left");
    }

    public void moveRight() {
        System.out.println(getName() + " Moving right");
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Our game is created, it defines the following actions:

  • move_up: which will display a message of a movement upwards.
  • move_down: which will display a message of a movement downwards.
  • move_left: which will display a message of a movement to the left.
  • move_right: which will display a message of a movement to the right.

But also a property that will define the name of the game, just to allow you to add several games of the same structure in the future.

Now, let's define the controller of our game, the controller. This controller will easily allow for the definition of several controllers in the future:

Controller

 package com.design.command.model;

import com.design.command.command.Command;

public class Controller {
    private Command upCommand, downCommand, leftCommand, rightCommand;

    public Controller(Command upCommand, Command downCommand, Command leftCommand, Command rightCommand) {
        //concrete Command
        upCommand = upCommand;
        downCommand = downCommand;
        leftCommand = leftCommand;
        rightCommand = rightCommand;
    }

    public void arrowUp() {
        upCommand.execute();
    }
    public void arrowDown() {
        downCommand.execute();
    }
    public void arrowLeft() {
        leftCommand.execute();
    }
    public void arrowRight() {
        rightCommand.execute();
    }
}

Our controller is defined! Now, as you can see, we need 4 commands to define the actions of our controller. These actions will react according to the receiver (game) they use:

ZeldaUpCommand

 package com.design.command.command.zelda;

import com.design.command.character.ZeldaCharacterReceiver;
import com.design.command.command.Command;

public class ZeldaUpCommand implements Command {

    private ZeldaCharacterReceiver zeldaCharacterReceiver;


    public ZeldaUpCommand(ZeldaCharacterReceiver zeldaCharacterReceiver) {
        this.zeldaCharacterReceiver = zeldaCharacterReceiver;
    }

    @Override
    public void execute() {
        zeldaCharacterReceiver.moveUp();
    }
}

ZeldaDownCommand

 package com.design.command.command.zelda;

import com.design.command.character.ZeldaCharacterReceiver;
import com.design.command.command.Command;

public class ZeldaDownCommand implements Command {

    private ZeldaCharacterReceiver zeldaCharacterReceiver;


    public ZeldaDownCommand(ZeldaCharacterReceiver zeldaCharacterReceiver) {
        this.zeldaCharacterReceiver = zeldaCharacterReceiver;
    }

    @Override
    public void execute() {
        zeldaCharacterReceiver.moveDown();
    }
}

ZeldaLeftCommand

 package com.design.command.command.zelda;

import com.design.command.character.ZeldaCharacterReceiver;
import com.design.command.command.Command;

public class ZeldaLeftCommand implements Command {

    private ZeldaCharacterReceiver zeldaCharacterReceiver;


    public ZeldaLeftCommand(ZeldaCharacterReceiver zeldaCharacterReceiver) {
        this.zeldaCharacterReceiver = zeldaCharacterReceiver;
    }

    @Override
    public void execute() {
        zeldaCharacterReceiver.moveLeft();
    }
}

ZeldaRightCommand

 package com.design.command.command.zelda;

import com.design.command.character.ZeldaCharacterReceiver;
import com.design.command.command.Command;

public classZeldaRightCommand implements Command {

    private ZeldaCharacterReceiver zeldaCharacterReceiver;


    public ZeldaRightCommand(ZeldaCharacterReceiver zeldaCharacterReceiver) {
        this.zeldaCharacterReceiver = zeldaCharacterReceiver;
    }

    @Override
    public void execute() {
        zeldaCharacterReceiver.moveRight();
    }
}

Great! Our 4 commands are implemented and each of them uses the receiver object assigned to it. This way, it will use the command defined above. Let's test our code in the Main class of our application:

Main

 import com.design.command.command.zelda.ZeldaUpCommand;
import com.design.command.model.Controller;

public class Main {
    public static void main(String[] args) {

        //Create receivers
        ZeldaCharacterReceiver zeldaCharacterReceiver = new ZeldaCharacterReceiver();
        zeldaCharacterReceiver.setName("Zelda");

        //Commands
        ZeldaUpCommand zeldaUpCommand = new ZeldaUpCommand(zeldaCharacterReceiver);
        ZeldaDownCommand zeldaDownCommand = new ZeldaDownCommand(zeldaCharacterReceiver);
        ZeldaLeftCommand zeldaLeftCommand = new ZeldaLeftCommand(zeldaCharacterReceiver);
        ZeldaRightCommand zeldaRightCommand = new ZeldaRightCommand(zeldaCharacterReceiver);

        //Invoker
        Controller nintendo = new Controller(zeldaUpCommand, zeldaDownCommand, zeldaLeftCommand, zeldaRightCommand);

        nintendo.arrowDown();
        nintendo.arrowLeft();
        nintendo.arrowRight();
        nintendo.arrowUp();

    }
}

Result:

 Zelda Jumping down
Zelda Moving left
Zelda Moving right
Zelda Jumping up

Here, we have finished our implementation. What we can notice:

  • We have different commands for our game, which will allow us to add more commands if we want to add a game.
  • Our controller only uses objects of type command, which will increase flexibility by taking commands implementing the interface.
  • Adding a game will be done in the same way as implementing Zelda.
  • We have gained flexibility where each action is a Command object.

Pierre Colart

Passionate developer and architect who wants to share their world and discoveries in order to make things simpler for everyone.

See profil

Latest posts

Sequences, Time Series and Prediction

© 2023 Switch case. Made with by Pierre Colart