Command Pattern in Haxe: Mastering Behavioral Design Patterns

Explore the Command Pattern in Haxe, a powerful behavioral design pattern that encapsulates requests as objects. Learn how to implement it for undo/redo functionality, task scheduling, and more in cross-platform applications.

6.3 Command Pattern

The Command Pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all the information about the request. This transformation allows for parameterization of clients with queues, requests, and operations, making it a versatile tool in software design. In this section, we will delve into the Command Pattern, its implementation in Haxe, and its practical applications.

Intent

The primary intent of the Command Pattern is to encapsulate a request as an object, thereby allowing for the parameterization of clients with queues, requests, and operations. This encapsulation provides several benefits, including the ability to:

  • Queue commands for later execution.
  • Support undoable operations.
  • Log changes for auditing purposes.
  • Implement callback functionality.

Key Participants

  1. Command Interface: Declares an interface for executing an operation.
  2. Concrete Command: Defines a binding between a Receiver object and an action. Implements the Command interface by invoking the corresponding operation(s) on the Receiver.
  3. Invoker: Asks the command to carry out the request.
  4. Receiver: Knows how to perform the operations associated with carrying out a request. Any class can serve as a Receiver.
  5. Client: Creates a ConcreteCommand object and sets its Receiver.

Applicability

Use the Command Pattern when you want to:

  • Parameterize objects with operations.
  • Specify, queue, and execute requests at different times.
  • Support undo operations.
  • Structure a system around high-level operations built on primitive operations.

Implementing Command in Haxe

Command Interface

In Haxe, we start by defining a Command interface with an execute method. This interface will be implemented by all concrete command classes.

1interface Command {
2    public function execute():Void;
3}

Concrete Command

Next, we create concrete command classes that implement the Command interface. Each concrete command will encapsulate a request by binding together a Receiver and an action.

 1class LightOnCommand implements Command {
 2    private var light:Light;
 3
 4    public function new(light:Light) {
 5        this.light = light;
 6    }
 7
 8    public function execute():Void {
 9        light.turnOn();
10    }
11}
12
13class LightOffCommand implements Command {
14    private var light:Light;
15
16    public function new(light:Light) {
17        this.light = light;
18    }
19
20    public function execute():Void {
21        light.turnOff();
22    }
23}

Receiver

The Receiver class contains the actual business logic that needs to be performed when a command is executed.

 1class Light {
 2    public function new() {}
 3
 4    public function turnOn():Void {
 5        trace("The light is on.");
 6    }
 7
 8    public function turnOff():Void {
 9        trace("The light is off.");
10    }
11}

Invoker

The Invoker class is responsible for executing commands. It can store commands and execute them at a later time.

 1class RemoteControl {
 2    private var command:Command;
 3
 4    public function new() {}
 5
 6    public function setCommand(command:Command):Void {
 7        this.command = command;
 8    }
 9
10    public function pressButton():Void {
11        command.execute();
12    }
13}

Client

Finally, the Client creates the concrete command objects and associates them with the appropriate Receiver.

 1class Main {
 2    static public function main() {
 3        var light = new Light();
 4        var lightOn = new LightOnCommand(light);
 5        var lightOff = new LightOffCommand(light);
 6
 7        var remote = new RemoteControl();
 8        remote.setCommand(lightOn);
 9        remote.pressButton(); // Output: The light is on.
10
11        remote.setCommand(lightOff);
12        remote.pressButton(); // Output: The light is off.
13    }
14}

Use Cases and Examples

Undo/Redo Functionality

One of the most common applications of the Command Pattern is implementing undo/redo functionality. By storing executed commands in a stack, we can easily reverse operations by executing an undo method on the command.

 1interface Command {
 2    public function execute():Void;
 3    public function undo():Void;
 4}
 5
 6class LightOnCommand implements Command {
 7    private var light:Light;
 8
 9    public function new(light:Light) {
10        this.light = light;
11    }
12
13    public function execute():Void {
14        light.turnOn();
15    }
16
17    public function undo():Void {
18        light.turnOff();
19    }
20}
21
22class LightOffCommand implements Command {
23    private var light:Light;
24
25    public function new(light:Light) {
26        this.light = light;
27    }
28
29    public function execute():Void {
30        light.turnOff();
31    }
32
33    public function undo():Void {
34        light.turnOn();
35    }
36}

Task Scheduling

The Command Pattern can also be used for task scheduling, where commands are queued for execution at specific times.

 1class Scheduler {
 2    private var commandQueue:Array<Command> = [];
 3
 4    public function new() {}
 5
 6    public function scheduleCommand(command:Command):Void {
 7        commandQueue.push(command);
 8    }
 9
10    public function run():Void {
11        for (command in commandQueue) {
12            command.execute();
13        }
14    }
15}

Design Considerations

  • Decoupling: The Command Pattern decouples the object that invokes the operation from the one that knows how to perform it.
  • Flexibility: It allows for easy addition of new commands without changing existing code.
  • Complexity: The pattern can introduce complexity due to the increased number of classes.

Differences and Similarities

The Command Pattern is often confused with the Strategy Pattern. While both encapsulate actions, the Command Pattern is more about encapsulating requests and operations, while the Strategy Pattern focuses on encapsulating algorithms.

Visualizing the Command Pattern

Below is a class diagram illustrating the relationships between the components of the Command Pattern.

    classDiagram
	    class Command {
	        <<interface>>
	        +execute() : Void
	    }
	    class ConcreteCommand {
	        +execute() : Void
	        +undo() : Void
	    }
	    class Invoker {
	        +setCommand(command: Command) : Void
	        +pressButton() : Void
	    }
	    class Receiver {
	        +action() : Void
	    }
	    class Client {
	        +main() : Void
	    }
	    Command <|-- ConcreteCommand
	    ConcreteCommand --> Receiver
	    Invoker --> Command
	    Client --> Invoker

Try It Yourself

Experiment with the Command Pattern by modifying the code examples. Try adding new commands, such as DimLightCommand, and implement an undo feature for the RemoteControl. Consider how you might extend the pattern to support macro commands that execute multiple commands in sequence.

Knowledge Check

Before moving on, let’s summarize the key takeaways:

  • The Command Pattern encapsulates requests as objects, allowing for flexible and decoupled design.
  • It is useful for implementing undo/redo functionality and task scheduling.
  • The pattern involves key participants: Command, Concrete Command, Invoker, Receiver, and Client.

Quiz Time!

Loading quiz…

Remember, mastering the Command Pattern in Haxe is just one step in your journey to becoming an expert cross-platform software engineer. Keep experimenting, stay curious, and enjoy the journey!

Revised on Thursday, April 23, 2026