Changeables desconstructed

Mon, Dec 22, 2003

Frank Hileman (no blog, Frank?) has been pounding us (the Avalon team) with some great questions.  Most of these have been conversations in comments, but I figured that I should bring this to the front page, so to speak.

Of of Frank's concerns, at least as expressed in my comments section, is that we are essentially over engineering with the Changeable pattern.  Specifically, he says this:

The non-intrusive part of Changeable has a const flag on stock brushes, etc. This means the user must clone these and write to a copy. This is simple and easy to understand.

The intrusive part of Changeable is the part which introduces the idea of "used" and changes the simple concept of "object reference" into something complicated. From what you have explained, the intrusive part of Changeable exists only to allow simultaneous multi-threaded writers to the scene graph.

I think that the point around threading is a red-herring here. 

Changeable Requirements

The main problem that changeables are trying to solve is that we want to have, under most circumstances, these changeable API objects (Brush, Pen, etc.) act like value types and other times we want them to act like refernce types.  We essentially want to walk a fine line and get advantages from each.  Frank, forgive me if you've heard a bunch of this before, but I think that this is useful to put together in one place.

With respect to value types, we want the simple user semantic to be: Modify my local copy and have it be disconnected or "severed" when I pass it in to another object.  When rendering this means that I can do this:

SolidColorBrush scb = new SolidColorBrush();
scb.Color = Colors.Blue;
myDrawingContext.DrawGeometry(scb, null /*pen*/, myGeometry1);
scb.Color = Colors.Red;
myDrawingContext.DrawGeometry(scb, null /*pen*/, myGeometry2);

and have my two geometries be drawn with different colors.  Since many times the drawing context is writing in to a metafile like data structure, if we used pure reference semantics both geometries would be drawn as Red. 

Another example is setting properties in to a set of elements:

SolidColor Brush scb = new SolidColorBrush();
scb.Color = Colors.Blue;
myElement1.Background = scb;
myElement2.Background = scb;

// in some other code...
((SolidColorBrush)myElement1.Background).Color = Colors.Red;

I think, in most cases, if some user walks up to an element, such as myElement1 above, and sets the background brush, he doesn't expect that this will have the side effect of changing the background brush of another element.  Side effects like this would make for an unstable system.

Value types don't solve all of our problems though!  We also want the efficiencies and advanatages of making these be reference types.  Specifically, we want to have to have hierarchies, not have to copy the entire thing around and be able to save space by having multiple places reference the same thing.  We imagine a world with lots of elements getting their values via styling.  Because of this, we want the normal case be that a user puts a value in to a style block somewhere and then that object (via reference) gets widely shared, perhaps between hundreds of elements.  We obviously want to make the per-reference cost as small as possible here.  Going with a pure value type system won't work.

The Builder Pattern

The first cut at solving this problem was called the Builder Pattern.  This pattern was a generalization of the StringBuilder pattern.  Specifically, for each type, there was a companion, or Builder type.  The main type was immutable.  In other words, any write operations would raise an exception.  So, for SolidColorBrush, we wrote a SolidColorBrushBuilder.  SolidColorBrush was immutable while SolidColorBrushBuilder was a fully writeable object.  All of the APIs in the system (property types on element, parameters in to DrawingContext methods, etc.) took the non-builder types.  To create one of those non-builder types, the user had to do one of the following:

  • Create the target type directly by using a constructor.
  • Create a builder, configure it, and call the ToXXX method on it to construct the main type.
  • Create a builder by initializing it off of its companion type.  Make a delta and call the ToXXX method to construct the main type.

From one point of view, this is a great system.  It is very explicit (at compile time) what is editable and what isn't.  However, some early users of our system (ad-hoc usability) ended up really disliking this system.  The need to constantly deal with both types was onourous.  It also meant that the user could never just index directly in to a type and change it.  To affect any change, it was always necessary to create the builder, make the change, and set the old value in.

The Changeable Pattern

In response to a hail of internal criticism on the Builder Pattern we went back and came up with the Changeable pattern.  The idea is that we would have one type object with an immutable bit.  Once the immutable bit gets set, all further changes to that object will throw.  This opens the way up to runtime errors (instead of compile time errors) but makes certain "delta" scenarios much easier to deal with.

However, we still wanted to solve the problems listed in the first part of this post.  Specifically, we didn't want to leave it up to the user to manually flip the immutable bit.  In this way we defined certain types of operations that would automatically create a copy and flip the immutable bit for you if necessary.  We then went further and added advanced modes so that those APIs either make a copy but don't flip the immutable bit or the APIs just take a reference.  These last modes (whereby something like an element holds on to a mutable brush) were never possible with the Builder pattern.

We also added a notification mechanism so that someone who does hold on to an object that isn't immutable can learn when that object changes.

So, do you guys (Frank?) like the more explicit builder pattern better?  More questions?