Saturday, January 30, 2010

The Command Pattern

Today, in this post I am going tot talk about the command pattern and implement it in Java. The command pattern is a behavioral pattern whose primary objective is to decouple the sender from the receiver in such a way that the sender is not even aware of the type of the receiver of the command.

So, lets see where this pattern can be used.

The command pattern can be used in situations where you need a perform a given task on a number of objects of different types. But in each of these objects, not only is the implementation dependent on the type of the object, but also the functionality is completely different from the other objects.

Lets illustrate with the help of an example.

Sample Scenario :

As in my other posts, I will try to explain the scenario by means of examples of a game.
I believe that many of us have played First Person Shooter(FPS) games where you go on a mission and clear each level by destroying several enemies by using ruthless gunpower (and sometimes good strategies as well). One thing that is common among most of the FPS is that you have a number of weapons and each one of them has a primary attack and a secondary attack. The primary attack and secondary attack are characteristic features of that specific weapon. But when you fire the weapon, all you do is click your left mouse button for primary attack and click your right mouse button for secondary attack. What happens when you click on the left/right mouse button is something that is weapon specific.

Now, lets map this scenario to the command pattern. The following requirements are clearly visible from the situation.
  1. The client(yourself), issues two types of commands - primary attack, secondary attack.
  2. Each weapon has different types of attack mechanisms that it can perform.
  3. For each weapon, you need to associate your primary attack and secondary attack to one of the predefined attack mechanisms of your weapon.

You need to decouple the sender's commands i.e. the primary attack and secondary attack command from knowing the actual attack mechanism being executed. This gives you the ability to associate the primary attack command with a different mechanism in the future. for example, if the user chooses to make his left click function as the right click and the right click function as the left click.

Here is a sample description of my game components.
I have 2 weapons in my game. The Plasma Gun and the Shotgun. The Plasma Gun has 2 modes of attack - normal fire and blast fire. The Shotgun has 2 modes of attack - Single shot attack, and dual shot attack. Initially, what I want to do is to make one of these mechanisms the primary attack and secondary attack for each weapon.

Let's see how this could have been done without the command pattern and the problems that would arise.

Firstly, you would declare a base class called Weapon, that would have 2 functions- primary attack and secondary attack. Then, you would subclass the Weapon class and create 2 new classes - Shotgun and PlasmaGun that would override the primary attack and secondary attack functions. Your client would create objects of the weapons and call the primary attack and secondary attack functions on the objects and the code for your weapon specific functionality would execute.

That's pretty simple eh?

Oh, but wait! What if I want to swap my mouse functions?? i.e. The left click becomes the right click? Or what if one fine day in the future I decide that each gun has 5 attack mechanisms, but the user has to select which mechanism can be activated by which input- right click or left click. Os suppose you have added a new  gun with a new attack mechanism and want to associate it with the middle click. What will you do then?? Will you add new methods to your Weapon class?? Will you use several boolean flags to determine what function will be called when? Or worse of all, will you resort to complex techniques such as reflection?

The issue here is that you certainly don't want your client want to know what changes have been made at the level of your Weapon classes. And also, you don't want your classes to be aware of the type of command issued by the client. i.e. the weapons should not be aware whether the client is issuing a primary attack command or a secondary attack command.

The Command Pattern to the rescue!

The command pattern decouples the client from the destination by acting as a middleman and converting the client request to the appropriate destination request. In the code that follows, I will demonstrate how to achieve this functionality. Lets first discuss what the code will do.

Firstly, I will create my Shotgun and PlasmaGun classes without the need of implementing any superclasses or superinterfaces for this scenario. The Shotgun class will have 2 mehtods- one to fire a single shot, and another to fire a dual shot. Similarly the PlasmaGun will have 2 methods - One for normal fire and the other for a blast fire. These classes are not aware of the concept of a primary attack and secondary attack.

Now, I will create a Command interface that has one method- execute(). Then I create classes that implement the Command interface, one class for each command issued to a gun. i.e. Primary/Seconday Attack for shotgun, and primary/seconday attack for PlasmaGun. So, 4 command classes. All these command classes have a private instance of the gun over which they will execute. In the execute function, we will call the appropriate function of the gun classes.

The beauty of this approach is that the Gun classes do not know what is a primary or a secondary attack, and the client does not need to know what is the exact method that will be called when is issues the primary and secondary attack command.

The problem with this approach is that the for each type of command, we will have to create new command classes. This can considerably increase the classes that you may need to manage and can become a problem if you have several such functionalities, However, being a design pattern, it is not bound to suit the requirement for all scenarios. So, its up to u to decide when and where the command pattern will be useful in your project.

Following is the implementation of the above scenario using the Command pattern in Java.


PlasmaGun.java

public class PlasmaGun {
    public boolean firePlasmaShot()
    {
        System.out.println("Plasma Fire : Normal");
        return true;
    }

    public boolean firePlasmaBlast()
    {
        System.out.println("Plasma Fire : Blast");
        return true;
    }
}


ShotGun.java

public class ShotGun {

    public boolean fireSingleShot() {
        System.out.println("ShotGun Fire : Single");
        return true;
    }

    public boolean fireTripleShot() {
        System.out.println("ShotGun Fire : Triple");
        return true;
    }
}


Command.java

public interface Command {
    public void execute();
}



PlasmaGunNormalFireCommand.java

public class PlasmaGunNormalFireCommand implements Command{
    private PlasmaGun gun;

    public PlasmaGunNormalFireCommand(PlasmaGun gun) {
        this.gun = gun;
    }

    public void execute() {
        gun.firePlasmaShot();
    }
}



PlasmaGunBlastFireCommand.java

public class PlasmaGunBlastFireCommand implements Command{
    private PlasmaGun gun;

    public PlasmaGunBlastFireCommand(PlasmaGun gun) {
        this.gun = gun;
    }

    public void execute() {
        gun.firePlasmaBlast();
    }
}



ShotGunSingleShotCommand.java

public class ShotGunSingleShotCommand implements Command{
    private ShotGun gun;

    public ShotGunSingleShotCommand(ShotGun gun) {
        this.gun = gun;
    }

    public void execute() {
        gun.fireSingleShot();
    }
}



ShotGunTripleShotCommand.java

public class ShotGunTripleShotCommand implements Command{
    private ShotGun gun;

    public ShotGunTripleShotCommand(ShotGun gun) {
        this.gun = gun;
    }

    public void execute() {
        gun.fireTripleShot();
    }
}



WeaponControl.java


public class WeaponControl {
    private Command primaryAttackCommand, secondaryAttackCommand;

    public WeaponControl(Command primaryAttackCommand, Command secondaryAttackCommand) {
        this.primaryAttackCommand = primaryAttackCommand;
        this.secondaryAttackCommand = secondaryAttackCommand;
    }

    public void primaryAttack()
    {
        primaryAttackCommand.execute();
    }

    public void secondaryAttack()
    {
        secondaryAttackCommand.execute();
    }
}


Main.java

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ShotGun sg=new ShotGun();
        ShotGunSingleShotCommand sgSingleShotCmd= new ShotGunSingleShotCommand(sg);
        ShotGunTripleShotCommand sgTripleShotCmd = new ShotGunTripleShotCommand(sg);

        PlasmaGun pg=new PlasmaGun();
        PlasmaGunNormalFireCommand pgNormalFireCmd= new PlasmaGunNormalFireCommand(pg);
        PlasmaGunBlastFireCommand pgBlastFireCmd = new PlasmaGunBlastFireCommand(pg);

        WeaponControl shotGunControl=new WeaponControl(sgSingleShotCmd, sgTripleShotCmd);
        WeaponControl plasmaGunControl=new WeaponControl(pgNormalFireCmd, pgBlastFireCmd);

        shotGunControl.primaryAttack();
        plasmaGunControl.secondaryAttack();

        shotGunControl.secondaryAttack();
        plasmaGunControl.primaryAttack();

    }

}


Signing Off
Ryan

No comments: