Thursday, November 12, 2015

Flyweight in Dart


I lack a clear idea what I want Design Patterns in Dart to be. Scholarly like the Gang of Four book? Visually stimulating like Head First Design Patterns? Whimsical? Likely some combination thereof, but until I decide, I explore.

Up tonight, I start with the Flyweight Pattern. It is a simple enough pattern—so simple that it hardly seems a pattern in some languages, but I need to start somewhere. And since the Wikipedia article was kind enough to include a lovely Ruby example, I start tonight by adapting that example into Dart.

The idea behind the Flyweight Pattern is to save storage space via specialized caching. In the Ruby example of decorating a tree with lamps, the goal is to limit the number of lamp objects that are created. After all, there might be hundreds of lamps on larger tree. There is no real value to creating 200 blue lamp objects, 400 green lamp objects, and 300 red lamp objects. The lamps are all the same—only the branch on the tree changes. In GoF terms, the lamp color is an intrinsic property and the branch on which they reside is extrinsic.

Working from the outside, I expect to create a new Tree, hang a bunch of lamps, and then report on it:
main() {
  var tree = new Tree()
    ..hang_lamp('red', 1)
    ..hang_lamp('blue', 1)
    ..hang_lamp('yellow', 1)
    ..hang_lamp('red', 2)
    ..hang_lamp('blue', 2)
    ..hang_lamp('yellow', 2)
    ..hang_lamp('red', 3)
    ..hang_lamp('blue', 3)
    ..hang_lamp('yellow', 3)
    ..hang_lamp('red', 4)
    ..hang_lamp('blue', 4)
    ..hang_lamp('yellow', 4)
    ..hang_lamp('red', 5)
    ..hang_lamp('blue', 5)
    ..hang_lamp('yellow', 5)
    ..hang_lamp('red', 6)
    ..hang_lamp('blue', 6)
    ..hang_lamp('yellow', 6)
    ..hang_lamp('red', 7)
    ..hang_lamp('blue', 7)
    ..hang_lamp('yellow', 7);

  print(tree.report);
}
Dart method cascades work nicely here to hang a bunch of lamps, but still return the original Tree object.

A skeleton of the Tree class might look like:
class Tree {
  int count=0;
  void hang_lamp(color, branch) {
    count++;
  }
  String get report => "Added ${count} lamps.";
}
I will still need to add a Dart equivalent of the LampFactory from the Wikipedia example, but so far, so good. I can report on the total number of lamps that are added to the tree:
Added 21 lamps.
The "flyweight" object will be the Lamp, whose intrinsic property (the color) does not change regardless of where it is placed on the tree. The class definition is wonderfully brief in Dart:
class Lamp {
  String color;
  Lamp(this.color);
}
I never thought I'd say this about Ruby, but the Ruby version from Wikipedia sure is wordy, isn't it?

The LampFactory in Dart is even more terse:
class LampFactory {
  var cache = {};
  Lamp find_lamp(color) {
    return cache.putIfAbsent(color, () => new Lamp(color));
  }
  int get total_number_of_lamps_made => cache.length;
}
The cache is a Map of objects whose key is the color of the lamp. If the color is already present in the cache, then the putIfAbsent() in find_lamp() returns it. If the lamp is not already present, then the second argument to putIfAbsent() is evaluated, added to the cache, and returned. That is a nice example of why I love Dart so much.

Last is the TreeBranch class, which has an associated branch number and needs to know how to hang a lamp:
class TreeBranch {
  int number;
  TreeBranch(this.number);
  void hang(lamp) {
    print("  Hangs ${lamp.color} on branch #${number}.");
  }
}
For now, hanging a lamp just prints to STDOUT a summary of the action.

That pretty much does it for the pattern. Back in the Tree class, I modify the hang_lamp() method to create a TreeBranch on which a factory Lamp can be hung. I also modify the report getter to report on the number of flyweight objects used:
class Tree {
  int count=0;
  var lamp_factory;

  Tree(){ lamp_factory = new LampFactory(); }

  void hang_lamp(color, branch_number) {
    new TreeBranch(branch_number)
      ..hang(lamp_factory.find_lamp(color));

    count++;
  }

  String get report => "Added ${count} lamps.\n"
    "Used ${lamp_factory.total_number_of_lamps_made} kinds of lamps.";
}
With that, I can now report:
Hangs red on branch #1.
  Hangs blue on branch #1.
  Hangs yellow on branch #1.
  Hangs red on branch #2.
  Hangs blue on branch #2.
  Hangs yellow on branch #2.
  Hangs red on branch #3.
  Hangs blue on branch #3.
  Hangs yellow on branch #3.
  Hangs red on branch #4.
  Hangs blue on branch #4.
  Hangs yellow on branch #4.
  Hangs red on branch #5.
  Hangs blue on branch #5.
  Hangs yellow on branch #5.
  Hangs red on branch #6.
  Hangs blue on branch #6.
  Hangs yellow on branch #6.
  Hangs red on branch #7.
  Hangs blue on branch #7.
  Hangs yellow on branch #7.
Added 21 lamps.
Used 3 kinds of lamps.
That was a quick and easy implementation of the Flyweight pattern in Dart. Up tomorrow, I may explore this pattern a little more. For now, this was a good day #1!


Day #1

No comments:

Post a Comment