Command Design Pattern Applied
Hi there!
Today i’m gonna share with you a really great programming design pattern. It has a lot of usages and it is one of my favorites. The programming degin pattern command has a huge variety of use cases. In this post we will see how to implement something from the real world.
We will implement an electronic car key to open, close doors, enable, disable alarms, open, close the garage door or to open and close the hood and trunk of your car.
The example i will show to you, is a very generic one, so you can always come back here, take it and use it in your applications.
The UML Command Pattern
As you know me, i always start my projects by showing the class diagram over a UML diagram. This help us to fix it overview the example in a nice, compact way.
Explaning the Details Programming Design
Our client is the Ferrari Owner (FerrariCleint). It has a CarKey. The CarKey has a generic MicroShip (Involker) that can be configurated with commands. The commands (OpenDoorCommand) itself have Action (DoorAction) to execute. The CarKey can configurate do and undo Commands. The NullObjectCommand belongs to the Null Object Design Pattern and it will be also used here. Let’s see in the code the implementation details now.
Command and MicroShip
The NullObjectCommand is used here to avoid null pointer exceptions and to execute nothing as long as no command has been defined.
public interface Command { void execute(); } public class MicroChip { protected Command[] onCommands; protected Command[] offCommands; public MicroChip(int commandQuantity) { onCommands = new Command[commandQuantity]; offCommands = new Command[commandQuantity]; Command nullObjecCommand = new NullObjectCommand(); for (int i = 0; i < commandQuantity; i++) { onCommands[i]=nullObjecCommand; offCommands[i]=nullObjecCommand; } } public void configureCommand(int position, Command on, Command off){ onCommands[position]=on; offCommands[position]=off; } public void executeOnCommand(int position){ onCommands[position].execute(); } public void executeOffCommand(int position){ offCommands[position].execute(); } protected class NullObjectCommand implements Command{ @Override public void execute() { // NULL-OBJECT-PATTERN } } }
Concrete Commands and Actions
Here we can see the concrete implementation of Actions and Commands.
public class Door { public void on(){ System.out.println("Opening car doors..."); } public void off(){ System.out.println("Closing car doors..."); } } public class OpenDoorCommand implements Command { private Door door; public OpenDoorCommand(Door door) { this.door = door; } @Override public void execute() { door.on(); } } public class CloseDoorCommand implements Command { private Door door; public CloseDoorCommand(Door door) { this.door =door; } @Override public void execute() { door.off(); } }
The Generic MicroShip
As you can see here, this implementation or this MicroShip can hold as many commands as you need and can be reused in any situation you may need. In this MicroShip bellow i have implemented more then only this OpenDoorCommand and CloseDoorCommand above, so you can see the power of it. It is up to you to implement other commands like i did.The cool thing here is the ability to do and undo things. To create as many commands and exucute as many actions as we need. The simplicity and beauty of this pattern fascinates me.
public class CarKey { private MicroChip microChip; public CarKey() { final int commandQuantity = 5; microChip = new MicroChip(commandQuantity); final Hood hood = new Hood(); final OpenHoodCommand openHoodCmd = new OpenHoodCommand(hood); final CloseHoodCommand closeHoodCmd = new CloseHoodCommand(hood); microChip.configureCommand(0, openHoodCmd, closeHoodCmd); final Door door = new Door(); final OpenDoorCommand openDoorCmd = new OpenDoorCommand(door); final CloseDoorCommand closeDoorCmd = new CloseDoorCommand(door); microChip.configureCommand(1, openDoorCmd, closeDoorCmd); final Garage garage = new Garage(); final OpenGarageCommand openGarageCmd = new OpenGarageCommand(garage); final CloseGarageCommand closeGarageCmd = new CloseGarageCommand(garage); microChip.configureCommand(2, openGarageCmd, closeGarageCmd); final Trunk trunk = new Trunk(); final OpenTrunkCommand openTrunkCmd = new OpenTrunkCommand(trunk); final CloseTrunkCommand closeTrunkCmd = new CloseTrunkCommand(trunk); microChip.configureCommand(3, openTrunkCmd, closeTrunkCmd); final Alarm alarm = new Alarm(); final EnableAlarmCommand enableAlarmCmd = new EnableAlarmCommand(alarm); final DisableAlarmCommand disableAlarmCmd = new DisableAlarmCommand(alarm); microChip.configureCommand(4, enableAlarmCmd, disableAlarmCmd); } public void openHood(){microChip.executeOnCommand(0);} public void closeHood(){microChip.executeOffCommand(0);} public void openDoor(){microChip.executeOnCommand(1);} public void closeDoor(){microChip.executeOffCommand(1);} public void openGarage(){microChip.executeOnCommand(2);} public void closeGarage(){microChip.executeOffCommand(2);} public void openTrunk(){microChip.executeOnCommand(3);} public void closeTrunk(){microChip.executeOffCommand(3);} public void enableAlarm(){microChip.executeOnCommand(4);} public void disableAlarm(){microChip.executeOffCommand(4);} }
The FerrariClient
Finally we can see the usage and power of this beautiful design pattern. In this example i implemented more than one command to show to you, how it could looks like.
public class FerrariClient { public static void main(String[] args) { final CarKey ferrariSwitchbladeKey = new CarKey(); ferrariSwitchbladeKey.openHood(); ferrariSwitchbladeKey.openGarage(); ferrariSwitchbladeKey.openTrunk(); ferrariSwitchbladeKey.openDoor(); ferrariSwitchbladeKey.enableAlarm(); System.out.println("-------------------------------"); ferrariSwitchbladeKey.closeHood(); ferrariSwitchbladeKey.closeGarage(); ferrariSwitchbladeKey.closeTrunk(); ferrariSwitchbladeKey.closeDoor(); ferrariSwitchbladeKey.disableAlarm(); } }
That’s all. Hope you like it!
Reference: | Command Design Pattern Applied from our JCG partner Ricardo Ferreira at the Clean Code Development – Quality Seal blog. |
nice, i like it, just a qualification. I think it should be better to use some kind of enum type in order to execute something like this:
microChip.executeOnCommand(0);
instead of to use 0, i think its more clear to use an enum.
Thank you very much.
excellent