0

I'm writing a simulation of a car that can receive commands and act on them and I'm trying to implement it using the command pattern.

class Car {
  move() {
    console.log('move');
  }
}

interface Command { execute():void; }

// there can be more commands like this. eg: TURN, ACCELERATE class MoveCommand implements Command { private car: Car;

constructor(car: Car) { this.car = car; }

execute() { this.car.move(); } }

class Invoker { private moveCommand: MoveCommand;

constructor(moveCommand: MoveCommand) { this.moveCommand = moveCommand; }

move() { this.moveCommand.execute(); } }

The client would issue commands to the car as below.

// client
const car = new Car();
const invoker = new Invoker(new MoveCommand(car));
invoker.move();

I want to modify this so that a series of commands (eg: MOVE, TURN) can be read from a text file and then executed. In this case, a command parser class will convert the text commands to an array of Command objects. I'm having a hard time thinking of a clean way the command parser can create Command objects since the commands need to know about the receiver. It seems that I can remove the receiver from the constructor of MoveCommand and pass it into the execute method instead, or I can have a setter to set the receiver after the Command has been instantiated, but it feels like I'm violating the commonly used standard for the command pattern. Is there a cleaner way to achieve this, or am I trying to use the command pattern in a situation where it doesn't fit in? If the latter is the case, what would be a better design pattern for this scenario?

Epsilon
  • 109

3 Answers3

1

You're correct in that the command pattern as normally described assumes that a command encapsulates all information that's needed for execution. As such, it can only be used to delay execution of operations or to make them exactly repeatable.

Your case is basically implementing a scripting language with the ability of running a stored sequence of commands with different receivers.

A slight extension of the pattern would make sense here: Execution of commands in some context. Depending on your application, the context may be just the receiver which should execute all the commands, or some context object that contains a number of available receivers ("variables"), which then need to be accessed symbolically.

The execute() method would take a context object as argument, and use that as the receiver or use it to find the receiver via the symbolic names stored in the command.

1

I presume from your description that all of the commands in the list refer to the same object (car). E.g., it's a small script for a car.

You don't actually need to create the commands right then when you read the file. Instead, for each command, produce, on the fly, a factory function that creates the command when given a car. You can then return a list of these, or you can wrap this list into a top level function that just does

// ...
// parse your text file, produce a 'factoryFunctions' list...
// I.e. 
// factoryFunctions.push((car) => new MoveCommand(car));      // "TURN"
// factoryFunctions.push((car) => new TurnCommand(car, TurnDirection.LEFT)); // "TURN LEFT"
// ...

const scriptFactory = (car: Car) => factoryFunctions.map(f => f(car)) return scriptFactory

Now you have a thing that takes a Car parameter and creates your script (a list of commands) on-demand that you can pass around in your application. So pass it to something that has access to the Car instance of your choice (cause it's usable with different cars), and make your Invoker accept a Command array:

// client 
// ('scriptFactory' is injected as a dependency, or is otherwise made accessible)

const car = new Car(); const invoker = new Invoker(scriptFactory(car)); invoker.move()

1

The idea of the command pattern is to decouple requests (commands), invokers (asking commands to be carried out) and receivers (objects on which the commands operate, i.e. Car).

A very common implementation is to define the receiver in the constructor of the command. But, it is not imposed by the pattern, and there are known alternatives:

  • it is perfectly acceptable to use a setReceiver(car:Car) method to defer to a later stage the setting of the receiver. So you could very well parse your command script, create the commands, and set their receivers as soon as the Car is known.
  • GoF also consider the possibility of more "intelligent" (understand "autonomous") commands, e.g. commands able to use find dynamically their receivers (page 238). This may be overkill here, but would allow to address more complex cases, especially if several cars could be envisaged in the script.

Last but not least, GoF suggests an interesting alternative for your command list : instead of a simple array of commands, you'd use a special kind of command called MacroCommand. The pseudo code would look like:

class MacroCommand implements Command {
  private car: Car;
  private commands : Command[];

setReceiver(c:Car) { this.car = c; for (var cmd of this.commands) cmd.setReceiver(c); } add(command: Command { ... } execute() { // calls execute for each command in the list this.commands.forEach (function(v){v.execute();}); } }

Post Scriptum: The variants of the command pattern seem more than sufficient to cover the needs. If your scripting language would grow more complex, e.g. use of several cars, conditionals on car attributes, etc -- in other words, if you'd have a real scripting language -- then the interpreter pattern could be a more relevant alternative. The actual Cars to operate on would then be provided in symbol table in a context object.

Christophe
  • 81,699