Command: Design Pattern[Part-5]

Imagine, you are a developer of an FPS video game team who is developing this game for Play station 5. The game consists of battles with online enemies using different types of guns and weapons.

Use case

you have been assigned a task to design controls. So, before starting the design, let’s note down some of the actions, which are required to be performed through controllers/Keys.

  1. fire — fire bullets.
  2. jump — jump from the hurdles.
  3. reload — reload the gun.
  4. Scope — Aim towards the target.
  5. weapon Change — change the weapon.

and there could be many more actions possible. There will be some other automated actions too,

  1. vibration.
  2. Led Lights change.

Design

The simplest solution could be, writing a simple class “Controller” and adding key Listeners/Button Listeners to it.

The issues with this approach are,

  1. The design is not modular. If we need to change the configuration, we need to change the base Controller class, which might cause an impact like breaking some code/use case.
  2. The Design is actually, device-specific. So, in the future, if your game is gonna release for PC too, you need to write new implementations to make controls, keyboard compatible.
  3. The internal functions like vibrate() and ledChange() should work, depends on the action. for example, a reload action would cause less vibration than firing. So, we need to have multiple implementations of the same functions, in the same class.

Good software design is often based on the principle of separation of concerns, which usually results in breaking a framework into layers.

The Command pattern

It suggests that External objects shouldn’t send these requests directly. Instead, you should extract all of the request details, such as the object being called, the name of the method, and the list of arguments into a separate command class with a single method that triggers this request.

you can think it of something like this,

But here, there is a problem. We can’t simply put the if-else on button instances in the fireAction() method, otherwise, it will become the same framework again.

Since The Command pattern suggests that External objects shouldn’t send these requests directly. Instead, we should extract all of the request details, from the request itself. So, We need to make the button class convey their commands/Actions by themselves. It is possible if, we define some guidelines regarding the request extraction and the system will follow them every time.

Implementation

Create an Interface: Let’s start by creating an interface for Actions. This interface is responsible for calling the actions, based on different button presses. The system needs to call the execute method every time.

The next step is to make your commands implement the same interface. Usually, it has just a single execution method that takes no parameters. This interface lets you use various commands with the same request sender, without coupling it to concrete classes of commands. Let’s define the Actions as classes.

Fire: For Different firing actions, you can have different types of vibrations and led change.

Reload: For Different reload actions, you can have different types of vibrations and led change

You can have similar implementations for other actions too.

Now let’s change the Controller class,

Our Design would look like this,

Let’s analyze whether our issues from the previous design got fixed or not.

  1. The design became a module. No change in any other class needed if I need to change any action.
  2. Since we have decoupled the calling of actions from buttons, now we can easily create implementations for any other device.
  3. The internal functions like vibrate() and ledChange() can work action specific. And their implementation can be changed without affecting other actions implementation.

Summary — In a Nutshell

A Command pattern is an object behavioral pattern that allows us to achieve complete decoupling between the sender and the receiver. A sender is an object that invokes an operation, and a receiver is an object that receives the request to execute a certain operation.

--

--