Friday, June 17, 2016

Using the factory pattern in a Dart abstract class

/*
 * This example demonstrates the use of interrelated parallel inheritance
 * hierarchies: a subclass in one hierarchy (A) contains a reference to
 * the base class of another hierarchy (B). When (A) subclass is
 * instantiated its reference to (B) is assigned an object of the correct
 * subclass of (B).
 *
 * In this case, when a CatStudio is instantiated, it knows to populate
 * its reference to Animal with a Cat.
 *
 * While it's possible to simply specify the Cat class in CatStudio, this
 * would force the AnimalStudio class to know about Cat.
 */

library app.models;

import "dart:mirrors";

class CatStudio extends AnimalStudio {
  final animalType = "Cat";
  CatStudio() : super();
}

class Cat extends AnimalBase {
  Cat() : super("Meow");
}

/* When instantiating a new AnimalStudio subclass in client code, it is
 * only necessary to specify animalType as a final String in order to
 * get an instance of the desired Animal subclass assigned to the
 * `animal` attribute. In turn, AnimalBase is not required to know
 * anything about Animal subclasses defined in the client code.
 */
abstract class AnimalStudio {
  Animal animal;
  final String animalType;

  String record() {
    print("$animalType: ${animal.sing()}");
  }

  AnimalStudio() {
    animal = new Animal(animalType);
  }
}

/* A factory is required here as the calling code (AnimalStudio) knows
 * only the name of the Animal subclass to instantiate. Dart's reflection
 * system is queried to retrieve, and instantiate, the Animal class
 * defined in the client code. The file with client code must contain
 * the "library app.models" statement.
 */
abstract class Animal {
  final String sound;
  String sing() => sound;

  factory Animal(String type) {
    LibraryMirror lib = currentMirrorSystem().findLibrary(
        new Symbol('app.models'));
    Map<Symbol, Mirror> classes = lib.declarations;
    ClassMirror cls = classes[new Symbol(type)];
    InstanceMirror inst = cls.newInstance(new Symbol(''), []);
    return inst.reflectee;
  }
}

/* This is the class that must be extended in client code when defining
 * Animal subclasses. When the `Animal` factory is called with the
 * `animalType` parameter, the constructor of the desired subclass is
 * called. This will result in the *generative* constructor of the
 * superclass being called. This would be `Animal()`, which doesn't
 * exist. A workaround for this is presented in various places which
 * calls for creating a generative constructor with a mangled name,
 * such as `_Animal()`. This is abstruse, however, and the presentation
 * herein is considered idiomatic Dart as seen in the standard libraries,
 * etc (reference needed).
 */
class AnimalBase implements Animal {
  final String sound;
  String sing() => sound;
  AnimalBase(this.sound);
}

void main() {
  new CatStudio().record();
}

Monday, January 4, 2016

Event handling in Polymer.dart

So, I finally managed to get all the basic functionality from the "Polymer and Dart Codelab Admin Console" into my fork. The last step was to implement a set of methods in the superclass elements. These methods are called in response to interaction with the elements such as clicking on buttons, etc.

As a bit of background, I decided it would be a worthwhile exercise to "abstract?" out as much of the code as possible from the Codelab elements into more general superclasses containing only "title" and "description" fields, and make use of these superclass elements in the Code lab.

It should be noted that my design is a bit quirky and is based on a deprecated version of Polymer.dart. Particularly, my custom elements subclass other custom elements, which is not possible in the latest Polymer.dart. Another aspect of my design is just plain wrong: I instantiate superclass elements *inside* of my subclass elements! For example:

<codelab-element attributes="editing" extends="item-element">
    <item-element>
        <!-- codelab property -->
        <!-- codelab property -->
    </item-element>
</codelab-element>

This allows for the insertion of codelab properties into <item-element> using the <content> tag in <item-element>. Polymer.dart doesn't complain, and Dartium is happy to render the result without error.

A challenging aspect of using polymorphism turned out to be getting the subclass methods to be called in response to events in the superclass elements. For example, when the "submit" button is clicked in <item-form> it's necessary that the CodelabElement::submit() event handler is called so that any CodelabElement-specific operations are performed before handing execution off to ItemElement::submit(). Naturally, if <item-element> calls "submit()", ItemElement::submit() is going to be executed. Not what we want.

So it finally occurred to me to add an ItemElement::submit() method. This then fires a new event, 'itemupdated', which is caught by <codelab-element> because it happens to be the parent.

So we have the kind of ridiculous situation where a subclass instance is a parent of the superclass instance. But it works.

While walking, today, it occurred to me that the design could be a bit cleaner by firing events directly from the superclass element which would be caught by the subclass parent, rather than the superclass. But it doesn't turn out to be possible to do this, as far as I can tell.</codelab-element></item-element></item-form></item-element></content></item-element>

Sunday, January 3, 2016

I've been hard at work, or at least at work, since my last blog post hammering the Polymer and Dart Codelab project into shape to my liking. I set myself upon a quest to abstract out as much as possible from the example Admin Console interface so as to ease the development of new interfaces. Sure, I could have just resigned myself to copy and past the entire project for each new project, but that would involve a lot of text substitutions and room for errors. Besides, how much fun is that?

The problem

After factoring out much of the code from the Codelab* classes and <codelab-*> custom elements into their respective Item* and <item-*> subclasses and getting it mostly working, there was the nasty problem of handling the situation wherein a superclass element such as ItemElement needs to instatiate a new Item based on the its own type. For example, CodelabElement::resetForm() requires that a new Codelab be instantiated to populate the new form. Since we're moving as much code as possible into the base classes, I wanted to also have CodelabElement's superclass ItemElement create the new Codelab instance. But how to do this without ItemElement requiring knowledge about its subclasses? I needed a way to instantiate a new Codelab based only on a text string to the superclass Item constructor.

Factory and mirrors to the rescue

Dart actually has the factory pattern built into the language. To use it, a superclass method is declared using the 'factory' keyword:

import 'package:polymer/polymer.dart';
import 'dart:mirrors';


class Item extends Observable {
  String _title;
  String _description;

  static const MIN_TITLE_LENGTH = 10;
  static const MAX_TITLE_LENGTH = 30;
  static const MAX_DESCRIPTION_LENGTH = 140;

  String get title => _title;
  set title(String s) => _title = s;

  String get description => _description;
  set description(String s) => _description = s;

   /// Allow a subclass instance to be generated with
   /// an empty argument list
  Item.created(this._title, this._description);

  factory Item(String type, String title, String description) {
    if(type == null)
      return; // Todo: Determ how this is being called w/ no type
    MirrorSystem libs = currentMirrorSystem();
    LibraryMirror lib = libs.findLibrary(new Symbol('polymer_and_dart.web.models'));
    Map<Symbol, Mirror> classes = lib.declarations;
    ClassMirror cls = classes[new Symbol(type)];
    InstanceMirror inst = cls.newInstance(new Symbol(''), [title, description]);
    return inst.reflectee;
  }
}
The code is pretty self-explanatory - the mirror system finds a list of class declarations in the "polymer_and_dart.web.models" namespace, and a ClassMirror instance is created with the one we want. This mirror is then used to invoke newInstance(), which returns a mirror on the new Codelab instance, contained in its "reflectee" attribute.

Some minor gotchas appeared when working out how to actually implement this in an inheritance hierarchy. For one, the concept of mixins and mixin applications needs to be clear in the mind of the developer. There's a decent discussion of how this works out in practical terms in the SO discussion at putting a factory method in an abstract class.

I need to think about this some more. There's the interesting aspect of this regarding making superclass attributes final...