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() { ListAnd that does it. I can play some wonderful tunes and, when one really strikes me, I can replay it later thanks to a memento: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]); }
$ ./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 YearI 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