Monday, December 7, 2015

Callback as Command


Per the Gang of Four book, the command pattern parameterizes objects by an object to perform. Also from the same book, commands are an object-oriented replacement for callback functions. I spent some time getting the object-oriented version working last night in Dart, tonight I replace it with the callback version.

In the client code, I am still using the same invoker and receiver from last night:
  // Invoker
  var s = new Switch();

  // Receiver
  var lamp = new Light();
Previously, I had stored the on/off actions on the lamp as commands:
  var switchUp = new OnCommand(lamp),
    switchDown = new OffCommand(lamp);
Those commands bind the receiver and an action as they are supposed to in this pattern:
class OnCommand implements Command {
  Light light;
  OnCommand(this.light);
  void execute() { light.turn('ON'); }
}
To replace this (and the OffCommand) with callbacks, I need to similarly bind receiver and action. That is a fairly easy task:
  // Commands
  var switchUp = (){ lamp.turn('ON'); },
    switchDown = (){ lamp.turn('OFF'); };
At the risk of saying something nice about callbacks and the spaghetti that they so often beget, I must admit that the callbacks are far more concise than the equivalent command objects. This is Dart, so the commands were not that much trouble. Still both callback-commands are one-liners. That is a definite improvement.

I am not quite done converting to callbacks. The command objects supported an execute() method. Dart functions all support a call() method to invoke the function. So the invoker code for executed the store command needs to invoke call() now:
class Switch {
  List<Function> _history = [];

  void storeAndExecute(Function c) {
    c.call();
    _history.add(c);
  }
  // ...
}
Similarly, the simple undo method also needs to use call():
  void undo() {
    _history.
      reversed.
      forEach((c) { c.call(); });
  }
With that, the code that invokes the invoker should work:
    switch (command) {
      case 'on':
        s.storeAndExecute(switchUp);
        break;
      case 'off':
        s.storeAndExecute(switchDown);
        break;
      // ...
    }
And work it does. I can turn the lamp on and off and reverse the order as desired:
$ ./bin/press_switch.dart on off on on                     
Light ON
Light OFF
Light ON
Light ON
--
Light ON
Light ON
Light OFF
Light ON
That is exactly how the command object behaved last night.

Is one way better than the other? The callback version tonight is a definite step up in clarity and lesser amount of code. I imagine that command objects still have their place. I will see if I can find them… starting tomorrow.


Play with this code on DartPad: https://dartpad.dartlang.org/719748d72bafeeefaab1.

Day #26

No comments:

Post a Comment