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.