What's a lonely geek to do late in the evening? Write about the Command Pattern of course...

Let's start with the definition: "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undo-able operations."

And now, time for the spec I just made up: Our UI has three buttons at the bottom of the screen, which contain various actions. These actions are different depending on the selected item however. We need to implement this piece of UI code, without it having any knowledge of our game units.

How would we approach this? Let's say these buttons are nothing more than placeholders, to be filled with real buttons whenever a unit is selected. Our game engine will respond to a selected event and create and pass on the real buttons to our UI code.

So, our UI code will only need to know about one thing, a button. Let's create an interface for this type of button, and coincidentally name it ICommand :)

Command Interface

We'll code our UI code against the ICommand, respecting to program against an interface and not an implementation.

At the end, we'll need some real implementations as well however. So let's create some "buttons", which I'll call Commands from now on.

Right now we only have two types of items, GameUnits and IBuildings. Each Command will perform an action against one of these, encapsulating the object through composition.

For this example, we can make a GameUnit turn Aggressive, Normal and Suicidal. We can also power buildings on and off.

Command Objects

Notice these Commands themselves are written against interfaces as well, our good habits are already starting to pay off.

The NoCommand is a little tricky, it's just a dummy object, which sometimes is called a [Null Object(http://en.wikipedia.org/wiki/NullObjectpattern "Wikipedia - Null Object Pattern") as well. It's being used for buttons which don't do anything, basically their default value.

It's time to create our UI code, the GameControls class. This class will contain the three buttons, ICommands, and helper methods to assign real buttons into the placeholder slots, as well as triggering button clicks.

Additionally, there's another requirement which has surfaced. We want to be able to undo our last action, whenever possible, can't undo suicide ;)

We'll accomplish this by storing the last ICommand, and when an undo action is needed, call the Undo() method on it, since each command provides the exact opposite of Execute() in it's Undo() method.

Invoker Object

That's all the code that was needed to implement the Command Pattern actually.

Our GameControls class has no idea GameUnit or IBuilding exists, it's totally decoupled from them.

By using Command objects, we can treat them as little blocks which can be stored in a queue for multiple level undo, we can let several threads pop commands of a queue to process them in parallel, and much more.

Let's have a look at the full diagram, with the official names written on them as well.

Command Pattern

Time to put our code into action and test it.

Let's imagine we have a full blown engine, and when a unit is selected, various events get fired, resulting in the creation of the correct ICommand objects, being passed to our GameControls UI code, which causes buttons to be displayed on screen. We'll also simulate a click on the various buttons and the undo button.

Testing Command Pattern

That's it! I've uploaded the solution for the Command Pattern so you can have a look at the code of the Command Objects and the details of the GameControls.

Questions? Feedback? Comment!!

Some additional information on the Command pattern:

Reacties: 1
  • Kyle

    Good stuff. I recently had to do something similar. We deal with multiple pieces of the same type of hardware, just ones made by different manufacturers. The key point that I realized was that: It does NOT matter what hardware we're sending to - our program can send a command to the hardware without having any idea what it's sending. And that's where the ICommand pattern came in handy. In fact, it turns out that for most hardware, we really only have about four commands. The reason it's this way is most of the hardware we have to deal with will only send a key value pair into the hardware, and get a key value pair out. Hence, I just have the UI be responsible for sending the correct values for the command (which it gets from the user), stow them in XML, and send it off to a simple command factory in order to build the correct command type based on the XML.

  • Reageer
    Items aangeduid met * zijn verplicht. (Naam, Email, Commentaar)
    Enkele items ontbreken of zijn fout ingevuld.
    Om zeker te zijn dat je geen computer bent, typ de onderstaande tekst over.