Example: Implementing methods from the Compensable interface
The CompensableCommand interface declares the getCompensatingCommand method that the application programmer must implement.
Method from the CompensableCommand interface in the ModifyCheckingAccountCmdImpl class
The following code example shows the implementation for the ModifyCheckingAccountCmd command. The implementation simply returns an instance of the ModifyCheckingAccountCompensatorCmd command that is associated with the current command.
public class ModifyCheckingAccountCmdImpl extends TargetableCommandImpl implements ModifyCheckingAccountCmd { ... // Method from CompensableCommand interface public Command getCompensatingCommand() throws CommandException { modifyCheckingAccountCompensatorCmd = new ModifyCheckingAccountCompensatorCmd(this); return (Command)modifyCheckingAccountCompensatorCmd; } }
Writing the compensating command
An application that uses a compensable command requires two separate commands: the primary command (declared as a CompensableCommand) and the compensating command. In the example application, the primary command is declared in the ModifyCheckingAccountCmd interface and implemented in the ModifyCheckingAccountCmdImpl class. Because this command is also a compensable command, there is a second command associated with it that is designed to undo its work. When you create a compensable command, you also have to write the compensating command. Writing a compensating command can require exactly the same steps as writing the original command: writing the interface and providing an implementation class. In some cases, it may be simpler. For example, the command to compensate for the ModifyCheckingAccountCmd does not require any methods beyond those defined for the original command, so it does not need an interface. The compensating command, called ModifyCheckingAccountCompensatorCmd, simply needs to be implemented in a class that extends the TargetableCommandImpl class. This class must:
- Provide a way to instantiate the command; the example uses a constructor.
- Implement the three required methods: isReadyToCallExecute and reset—both from the Command interface and performExecute—from the TargetableCommand interface.
The following code example shows the structure of the implementation class, its variables (references to the original command and to the relevant checking account), and the constructor. The constructor simply instantiates the references to the primary command and account.
... public class ModifyCheckingAccountCompensatorCmd extends TargetableCommandImpl { public ModifyCheckingAccountCmdImpl modifyCheckingAccountCmdImpl; public CheckingAccount checkingAccount; public ModifyCheckingAccountCompensatorCmd( ModifyCheckingAccountCmdImpl originalCmd) { // Get an instance of the original command modifyCheckingAccountCmdImpl = originalCmd; // Get the relevant account checkingAccount = originalCmd.getCheckingAccount(); } // Methods from the Command and Targetable Command interfaces .... }The performExecute method verifies that the actual checking-account balance is consistent with what the original command returns. If so, it replaces the current balance with the previously stored balance by using the ModifyCheckingAccountCmd command. Finally, it saves the most-recent balances in case the compensating command needs to be undone. The reset method has no work to do. The following code example shows the implementation of the inherited methods. The implementation of the isReadyToCallExecute method ensures that the checkingAccount variable has been instantiated.
... public class ModifyCheckingAccountCompensatorCmd extends TargetableCommandImpl { // Variables and constructor .... // Methods from the Command and TargetableCommand interfaces public boolean isReadyToCallExecute() { if (checkingAccount != null) return true; else return false; } public void performExecute() throws CommandException { try { ModifyCheckingAccountCmdImpl originalCmd = modifyCheckingAccountCmdImpl; // Retrieve the checking account modified by the original command CheckingAccount checkingAccount = originalCmd.getCheckingAccount(); if (modifyCheckingAccountCmdImpl.balance == checkingAccount.getBalance()) { // Reset the values on the original command checkingAccount.setBalance(originalCmd.oldBalance); float temp = modifyCheckingAccountCmdImpl.balance; originalCmd.balance = originalCmd.oldBalance; originalCmd.oldBalance = temp; } else { // Balances are inconsistent, so we cannot compensate throw new CommandException( "Object modified since this command ran."); } } catch (Exception e) { System.out.println(e.getMessage()); } } public void reset() {} }
Related tasks
Implementing command interfaces
Reference topic