Friday, November 27, 2015

A Better Memento Example: Now with More Encapsulation!


Up tonight, I take a closer look at encapsulation in the memento pattern. I have a decent start on the pattern from last night, but I glossed over one of the important reasons behind the memento pattern. Per the Gang of Four book, the pattern is applicable when obtaining state would break encapsulation.

Currently, the caretaker of the pattern plays a song, remembers the state on occasion, and replays particularly good Mel Tormé songs:
  List<String> replayer = [];
  var scatMan = new VelvetFogMachine();
  // ...
  scatMan.play(
      'New York, New York Medley',
      'A Vintage Year'
  );
  replayer.add(scatMan.nowPlaying);
  // ...

  // The New York, New York Medley with George Shearing really is wonderful
  scatMan.backTo(replayer[1]);
The encapsulation in this case is in the nowPlaying getter of the VelvetFogMachine. The nowPlaying property in the Melvie-tastic VelvetFogMachine is a list of strings (the title and album of the song):
class VelvetFogMachine {
  List<String> _nowPlaying;
  // ...
}
There is not much to the state being exposed and what state is exposed comes directly from the caretaker (i.e. the title and album). So this may not be the ideal example. In addition to the song playing, I will try storing the current time within the song as well:
// The Memento
class Playing {
  String title, album;
  double time;
  Playing(this.title, this.album, this.time);
}
If I tried to store this in the caretaker context, then I would certainly be breaking encapsulation. I would have to note the title, album and time at which the playing moved to the next song. Those aren't exactly related values, so this seems a better example.

Since I do not wish to expose the underlying implementation, the replayer can no longer be a list of strings. Instead, it needs to be a list of Playing objects (i.e. a list of mementos):
  List<Playing> replayer = [];
Nothing else needs to change in the caretaker code. The scatMan still plays songs by title and album. The nowPlaying property still returns a memento—albeit a more complex one. The backTo() method still takes the memento and uses it to restore state. Nothing really changed, but the more complex example better justifies the application of the pattern.

The code in VeletFogMachine does not change much—it just needs to accommodate time now. The play() method optionally accepts it:
  void play(String title, String album, [double time = 0.0]) {
    print("Playing $title // $album @ ${time.toStringAsFixed(2)}");
    _nowPlaying = new Playing(title, album, time);
  }
And the backTo() method needs to use the stored value to resume playing where it left off:
  // Restore from memento
  void backTo(Playing memento) {
    print("  *** Whoa! This was a good one, let's hear it again :) ***");
    play(memento.title, memento.album, memento.time);
  }
After adding a random number for the saved time, I have everything working—including restoring from the slightly more complex state:
$ ./bin/play_melvie.dart                                       
...
Playing The Lady is a Tramp // The Velvet Frog: The Very Best of Mel Tormé @ 0.00
  *** Whoa! This was a good one, let's hear it again :) ***
Playing New York, New York Medley // A Vintage Year @ 1.28
That seems a good place to stop for tonight. The memento seems a relatively simple pattern, which is making me feel the urge to move on to other patterns. But it is probably worth exploring a few more facets of the pattern in the next day or so. First up, I plan to see if there is a more "Darty" solution. Tomorrow.

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


Day #16

No comments:

Post a Comment