Command Pattern - TypeScript
Simple Remote Control
You have been asked to program a simple remote control. Simple remote control has only two buttons. One button to switch a device on
, and another button to switch a device off
.
Requirement
Customer is expecting to use this remote control to control various devices (one at at time).
For example:
Light
- To switchOn
orOff
a lightGarage Door
- ToOpen
orClose
a garage door.
Customer has plans to add more devices to be controlled using the same remote control. Customer should be able to easily swap the devices assigned to the remote control.
Vendor Classes
Customer has provided code for the two devices.
class Light {
public on() {
console.log('light switched on');
}
public off() {
console.log('light is switched off');
}
}
class GarageDoor {
public open() {
console.log('Garage door opened');
}
public close() {
console.log('Garage door closed');
}
}
Approach
From the above we can see that vendor classes do not have a common interface. Light
class has on
and off
methods where as GarageDoor has open
and close
methods. Same will be the case for any future devices the customer is planning to add.
One way of implementing the solution is to use if/ else to check which device is loaded on to the slot and then call the right methods.
if (slot === "Light") {
//call Off and On methods
} else if (slot === "GarageDoor") {
// call Open and Close methods
}
This approach tightly couples the client program with vendor classes. As we know, this approach will not scale and it is not easy to maintain.
In order for the remote control to be decoupled from the vendor classes, we need to introduce A Common Interface
between the remote control and vendor classes.
How do we achive this ? The answer is Command
pattern !
Command Pattern
Command pattern provides an interface which allows us to decouple the requester (remote control) of an action from the object that actually performs the action (Light or Ggrage Door). High level logical diagram below shows how it works.
A command object will be introduced into the design. Command object encapsulates a request to do something on a specific object. When the customer presses a button on the remote, we ask the command object to run an interface method ex: execute()
. Remote does not have any idea what the work is, it just has a command object that knows how to talk to the right object to get the work done. Hence the remote is decoupled from the actual object (ex: Light)
A sequence diagram explaing the pattem is given below.
Code
Lets see how we can program the remote using Command
pattern.
First we need an interface.
interface Command {
execute();
}
Then we need a command to turn on the light. Note that the command implements Command interface.
class LightONCommand implements Command {
light: Light;
constructor(light: Light) {
this.light = light;
}
execute(): void {
this.light.on();
}
}
When the customer presses a button on the remote, we call execute()
method which in turn calls the on()
method of light instance !
Now we need a Remote Control.
class SimpleRemoteControl {
slot: Command;
setCommand(cmd: Command) {
this.slot = cmd;
}
pressButton() {
slot.execute();
}
}
and Client Program to test the remote control.
const simpleRemote: SimpleRemoteControl = new SimpleRemoteControl(); // Invoker
const light = new Light(); // Receiver
const lightONCommand: Command = new LightONCommand(light); // Command object, with Command interface
simpleRemote.setCommand(lightONCommand); // load the command
simpleRemote.pressButton(); // runs execute() on Command object, which in turn swithes ON the light
Object Oriented Principles
We can see the following OOPs are used by Command pattern.
Program to interface - Client program depends only on
Command
interface. Any change to the Receiver objects (ex: Light) do not affect the client program.Encapsulation - Command object encapsulates the Receiver object (Light instance), hence the client is unaware of the actual object which is responsible to carry out the action.
Install and Run
Link to GitHub repository:
https://github.com/smarigowda/CommandPatternTypeScript/tree/simpleRemoteLightONAndOFF
Clone my GitHub repository.
git clone git@github.com:smarigowda/CommandPatternTypeScript.git
Checkout the branch.
git checkout simpleRemoteLightONAndOFF
Install node packages.
yarn install
or
npm install
Run the program
yarn test
or
npm run test
Program and Output
Main program:
Program output: