Thursday, December 3, 2015

An Iterator Memento


I can't see any good reason to try this... So of course I'll try it anyway.

The Gang of Four book includes a memento pattern iterator inspired by Dylan. Dart supports iterators natively, so there is no need for adapting this approach, but what the hey?

I start with the memento. In this implementation, the memento is the iteration state. For illustration purposes, this state will be a simple counter:
class IterationState {
  int index = 0;
  void increment() { index++; }
}
This example might benefit from a more complex state, but I'll start simply to get things working.

Pulling almost directly from the Gang of Four book, I declare the iterator collection as:
class SongCollection {
  List _list = [];

  SongCollection();

  IterationState createInitialState() => new IterationState();
  void next(IterationState state) { state.increment(); }
  bool isDone(IterationState state) => state.index >= _list.length;
  Song currentItem(IterationState state) => _list[state.index];

  void add(Song s) { _list.add(s); }
}
The private _list is appended via the add() method. Everything else in there works with the memento.

I continue to use the same Song class from the past couple of days:
class Song {
  String title, album;
  Song(this.title, this.album);
  void play() {
    print("Playing ${toString()}");
  }
  String toString() => "$title // $album";
}
Instances of this object will be added to the SongCollection iterator so that each be played. Hopefully that will do it for the setup.

In the caretaker context, I create the collection and add a choice selection of great songs:
  var collection = new SongCollection();
  collection
    ..add(new Song('The Lady is a Tramp', 'The Velvet Frog: The Very Best of Mel Tormé'))
    ..add(new Song('New York, New York Medley', 'A Vintage Year'))
    ..add(new Song('Blue Moon', 'The Velvet Frog: The Very Best of Mel Tormé'))
    ..add(new Song('\'Round Midnight', 'Tormé'));
Next, I grab a reference to the initial collection state (also know as the memento):
  var state = collection.createInitialState();
With that, I am able to iterate over the collection:
  while (!collection.isDone(state)) {
    collection.currentItem(state).play();
    collection.next(state);
  }
And that works as expected. Each song is played in the order in which is was added to the collection:
$ ./bin/play_melvie.dart
Playing The Lady is a Tramp // The Velvet Frog: The Very Best of Mel Tormé
Playing New York, New York Medley // A Vintage Year
Playing Blue Moon // The Velvet Frog: The Very Best of Mel Tormé
Playing 'Round Midnight // Tormé
On the face of it, there is little benefit to this approach. The forEach() of a Dart List could accomplish much the same behavior with no additional classes. That said, as the Gang of Four book mentions, it would be possible for multiple state mementos to work on the same collection (e.g. if multiple fans wanted to listen to the Velvet Fog at their convenience). Additionally, the internals of the state are not exposed to the caretaker—only the SongCollection has access to it, preserving encapsulation. This is not of much benefit when the state is a tracking integer, but more complex state mementos would certainly benefit from this approach.

In the end, this was a fun exercise. But I'll probably stick with forEach() all the same.

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


Day #22

No comments:

Post a Comment