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?

2
  • 1
    I would recommend looking for the well-known Mars Rover Kata. Not because it's THE solution, but because it poses the same problem. You will find that many people have solved this issue with the Command pattern, as you pretend. Spoiler, the receiver is who takes the commands and parses them into a sequence of concrete commands and rejects unsupported ones. The parser is injected into the receiver by the invoker when this last is created. Don't know if you have considered this solution. If you did and you discarded it, it would be good to let us know why.
    – Laiv
    Commented Jun 21, 2022 at 9:32
  • The key question here is: Do you want your “command script” to be tied to a specific car? Or do you expect the script to be generic and applicable to any car? Moreover, do you envisage conditionals in the script?
    – Christophe
    Commented Jun 21, 2022 at 10:51

3 Answers 3

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.

2
  • 1
    Would not that transform the command pattern into the "Interpreter" one?
    – Laiv
    Commented Jun 21, 2022 at 9:49
  • You're right, I must admit I've not really studied the various patterns for a long time :-) Commented Jun 21, 2022 at 10:17
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()
0
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.

1
  • Just a quick comment for the benefit of the OP: MacroCommand is basically an example of the Composite pattern applied to commands. If you opt for the lambdas (factories) approach that I described, you can also make use of this - instead of returning an array of commands, you return a MacroCommand that wraps the individual commands. Commented Jun 22, 2022 at 12:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.