Rusty Divine

Live, Love, Learn, Teach

Learning Design Patterns – Command Pattern

This week I'll be taking charge of the Command Pattern in my series on design patterns plucked fromthis book and from examples on the Internet.

What is it?

A simple analogy for the Command Pattern is a remote control. When you are clicking through your 637 channels looking for something in English that isn't tying to sell you something or change your political or religious point of view, you're giving commands to your remote, which then tells the system to increment the channel by one.

And when you use a universal remote and set your TV to channel 3, your VCR to channel F1, your sound system to Video, and then turn on your DVD player to watch a movie you rented from Blockbuster because nothing is on TV; your remote is issuing commands like "On" to each device, and the device is responsible for taking care of the details of turning itself on. If your remote was hardwired to turn on each component, you would have to open it up and add more code each time you switched out one TV for another – that's tightly coupled!

The Command Pattern can include more than just the ability to encapsulate requests; it can also queue up commands and log them, and support an "Undo" operation. By extending the pattern slightly, you can also create the ability to run a macro command that executes several commands at once – imagine a universal remote that has an "all-on" button that turns all of your devices on at once.

Where is it used?

This week I'd like to try something new for this section. I'd like you to add a comment to say where you've seen or used the Command Pattern – I'd enjoy hearing some real world scenarios, and it would help illuminate the pattern for everyone.

But why?

One of the fundamental rules of good design is to not have to open up old code to change it because it can result in unexpected consequences. The Command Pattern helps you extend your code by encapsulating method invocations, allowing you to add new objects that implement the command interface without having to monkey around with existing code and adding methods to control the new objects.

OK, and how is it implemented?

//Java
//Create a TV class that knows how to turn itself on and off
public class TV() {
	public TV() {
		system.out.println("TV Created");
	}

	public on() {
		system.out.println("TV On");
	}

	public off() {
		system.out.println("TV Off");
	}
}

// The command interface will be implemented by the command objects
public interface Command {
	public void execute();
	public void undo();
}

// Add a command for turning the TV on
public class TVOnCommand implements Command {
	TV tv;

	public TVOnCommand(TV tv){
		this.tv = tv;
	}

	public void execute() {
		tv.on();

	}

	public void undo() {
		tv.off()
	}
}
 
// Add a command for turning the TV off
public class TVOffCommand implements Command {
	TV tv;

	public TVOffCommand(TV tv){
		this.tv = tv;
	}

	public void execute() {
		tv.off();
	}

	public void undo() {
		//undo the off by turning back on
		tv.on()
	}
}

// Create a simple remote
public class SimpleRemote {
	Command onButton;
	Command offButton;
	Command undoCommand;

	public SimpleRemote() {}

	public void setOnCommand(Command command) {
		onButton = command;
	}

	public void setOffCommand(Command command) {
		offButton = command;
	}

	// The remote doesn't care what device it's turning on, it just issues the command

	// set the undo command object to the last object called
	public void buttonOnWasPressed() {
		onButton.execute();
		undoCommand = onButton;
	}

	public void buttonOffWasPressed() {
		offButton.execute();
		undoCommand = offButton;
	}

	public void buttonUndoWasPressed() {
		undoCommand.undo();
	}
}

public class RemoteTest {

	public static void main(String[] args) {
		SimpleRemote remote = new SimpleRemote();
		TV tv = new TV();
		TVOnCommand tvOn = new TVOnCommand(tv);
		TVOffCommand tvOff = new TVOffCommand(tv);

		//program the remote to turn the TV on and off
		remote.setOnCommand(tvOn);
		remote.setOffCommand(tvOff);

		remote.buttonOnWasPressed();
		remote.buttonOffWasPressed();
 
		//turn the TV back on by undoing the off call
		remote.buttonUndowWasPressed();
	}
}

//To implement a macro command that can turn on all devices, e.g.
public class MacroCommand implements Command {
	Command[] commands;

	public MacroCommand(Command[] commands) {
		this.commands = commands;
	}
 
	public void execute() {
		for (int i = 0; i < commands.length; i++) {
			commands[i].execute();
		}
	}

	public void undo() {
		for (int i = 0; i < commads.length; i++) {
			commands[i].undo();
		}
	}
}
blog comments powered by Disqus