Java – Passing context to the execute method in command design pattern

design-patternsjava

Going through the Command design pattern I understand that we need to create a Command by setting the context via constructor and then calling the execute method to perform some action on the context. Example:

public class Command implements ICommand {

    Device device;

    public Command(Device device) {
        this.device = device;
    }

    public void execute() {
        this.device.turnOn()
    }
}

I was wondering that with this approach we will be required to create a new Command object for every device object we create. Will it be ok to pass the context and some parameters to the execute method? I am looking for something like:

public class Command implements ICommand {

    public void execute(Device device) {
        this.device.turnOn();
    }

}

Are there any issues with this approach?

Best Answer

Yes it's absolutely OK to add parameters to the execute() method. It's good to remember how and why the command pattern is used: as a kind of lambda function. In Java 8, it is often no longer necessary to implement a concrete command class because we can use lambdas or method references instead, assuming that ICommand is a @FunctionalInterface.

A command object may save some context in instance fields and a lambda may capture enclosing variables, but the execute method can still take extra parameters and even return values! Just take care to define the Command interface in a way that solves your problems. This could even mean that the Command interface requires multiple methods, in which case it can no longer be a functional interface.

As the traditional example for the command pattern. we might have a GUI where the user clicks on some button. We give the button a Command to execute. The button doesn't supply any data and can't use any return value, so we might define

@FunctionalInterface
interface ButtonCommand {
  void execute();
}

In a completely different scenario, we might have an event driven account balance system. A command will receive the old account state and return the new account state. Then our design might look like:

@FunctionalInterface
interface AccountCommand {
  int execute(int balance);
}

class SendCommand implements AccountCommand {
  int amount;
  public SendCommand(int amount) { this.amount = amount; }
  @Override public int execute(int balance) { return balance - amount; }
}

class ReceiveCommand implements AccountCommand {
  int amount;
  public ReceiveCommand(int amount) { this.amount = amount; }
  @Override public int execute(int balance) { return balance + amount; }
}

class Account {
  int balance = 0;
  public int getBalance() { return balance; }
  public void executeCommand(AccountCommand command) {
    balance = command.execute(balance);
  } 
}

public static void main(String[] argv) {
  AccountCommand[] events = {
    new ReceiveCommand(70),
    // custom command for interest
    balance -> (int)(balance * 1.02),
    new SendCommand(30),
    new SendCommand(20),
  };
  Account acc = new Account();
  for (AccountCommand cmd : events) {
    int oldBalance = acc.getBalance();
    acc.executeCommand(cmd);
    int newBalance = acc.getBalance();
    System.out.println("Account balance changed from " + oldBalance + " to " + newBalance);
  }
}

It may of course be desirable to add further metadata to these commands (such as the transaction counterparty or the authorization for this command). The account could also keep a log of the applied commands, undo commands, and so on. So the command pattern can be more flexible than a simple lambda.