Thursday, November 26, 2015

The Velvet Fog Memento


I'm picking patterns to explore for Design Patterns in Dart by throwing proverbial darts at whatever design pattern book happens to be closest at hand. Up tonight is the memento.

I don't much care for the abstract nature of the examples in the Wikipedia article on memento. The constraint solver example in the GoF book always seemed too academic to me (though I recognize the importance of constraint solvers). I would prefer an example to which everyone can relate. Maybe something like a machine dedicated to building the ultimate Mel Tormé playlist. Everybody can relate to Melvie.

The "originator" in this scenario will be the VelvetFogMachine. It will be responsible for playing the best of the most gifted scatman of the 20th century. For this first pass, I will forgo the usual method names in the memento pattern (e.g. `CreateMemento`, `restoreFromMemento`). Instead, I would like to explore with more natural names like play(), backTo(), and nowPlaying:
// The "Originator"
class VelvetFogMachine {
  // Set the state
  void play(String title, String album);

  // Return a memento for possible later restoration
  Memento get nowPlaying;

  // Restore from memento
  void backTo(Memento memento);
}
The nowPlaying method will return a memento object that can be used to restore the currently playing song (i.e. the state) at a later time with backTo().

I will worry about proper serialization another day. For now, I make the Memento object as simple as possible. It encapsulates a string representation of the state:
class Memento {
  String state;
  Memento(this.state);
}
Back in the VelvetFogMachine originator, the play() method prints the currently playing song and stores the internal state:
class VelvetFogMachine {
  List<String> _nowPlaying;

  void play(String title, String album) {
    print("Playing $title // $album");
    _nowPlaying = [title, album];
  }
  // ...
}
As for generating a memento, I join the song and album with triple-colons in a string:
Memento get nowPlaying => new Memento(_nowPlaying.join(':::'));
Similarly, going back to a previous state needs to split the triple-colons and change the VelvetFogMachine with its own call to play():
  void backTo(Memento memento) {
    print("  *** Whoa! This was a good one, let's hear it again :) ***");
    var previous = memento.state.split(':::');
    play(previous[0], previous[1]);
  }
That more-or-less covers the functionality of the originator (VelvetFogMachine) and memento (Memento) in the pattern. All that is left is the caretakers. For this first pass, I put the caretaker back in the main() entry point.

Here, I need a list of possible replay targets and an instance of the VelvetFogMachine to play that scat music, save my place on occasion, and replay the very best moments:
main() {
  List replayer = [];

  var scatter = new VelvetFogMachine();
  scatter.play(
      'Blue Moon',
      'The Velvet Frog: The Very Best of Mel Tormé'
  );
  scatter.play(
      '\'Round Midnight',
      'Tormé'
  );
  replayer.add(scatter.nowPlaying);

  scatter.play(
      'It Don\'t Mean A Thing (If It Ain\'t Got That Swing)',
      'Best Of/ 20th Century'
  );
  scatter.play(
      'New York, New York Medley',
      'A Vintage Year'
  );
  replayer.add(scatter.nowPlaying);

  scatter.play(
      'The Lady is a Tramp',
      'The Velvet Frog: The Very Best of Mel Tormé'
  );

  // The New York, New York Medley with George Shearing really is wonderful
  scatter.backTo(replayer[1]);
}
And that does it. I can play some wonderful tunes and, when one really strikes me, I can replay it later thanks to a memento:
$ ./bin/play_melvie.dart
Playing Blue Moon // The Velvet Frog: The Very Best of Mel Tormé
Playing 'Round Midnight // Tormé
Playing It Don't Mean A Thing (If It Ain't Got That Swing) // Best Of/ 20th Century
Playing New York, New York Medley // A Vintage Year
Playing The Lady is a Tramp //  The Velvet Frog: The Very Best of Mel Tormé
  *** Whoa! This was a good one, let's hear it again :) ***
Playing New York, New York Medley // A Vintage Year
I do not know that there is much more to explore in this pattern. Where to serialize should be addressed (maybe in the Memento?). I have more general questions about how to present this—I am not fond of UML and prefer real-world examples over abstract ones. Still, I ought to be able to present how this generalizes for usage elsewhere—it is a pattern after all. Last, I need to see if I can find ways in which the pattern can be "Dartier." But for now, this is a fine stopping place.

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


Day #15

No comments:

Post a Comment