Design-by-Contract in RobotLegs 1.4
by Paul Taylor, Jul. 5, 2011, under [ actionscript, bitching, community ]

If you talked to me at a conference sometime in the last nine months, chances are I ranted loudly about type-variant auto-mediation. At the time, I meant an implementation I wrote in PureMVC because I assumed robotlegs already supported covariant auto-mediation. It doesn’t. Explanation to follow.

Flash and the City

Before I begin, this was the topic of my Flash and the City talk this year. You can find the slides here if you want.

Auto Mediation

Robotlegs has an interface called IMediatorMap, which maps UIComponent types to Mediator types. Robotlegs watches the display list, and when instances of any mapped components are added to (or removed from) the stage, it will create (or destroy) an instance of the mapped Mediator for it.

mediatorMap.mapView(MyViewType, MyViewMediator);

Each time you create and add a new instance of MyViewType to the stage, a corresponding MyViewMediator instance will be created.

Problems

The problem with MediatorMap is two-fold:

  1. It enforces an arbitrary restriction that each View instance can only have one Mediator instance. This is dangerous, because it blurs the line between view logic and business logic. Does this functionality belong in the UIComponent or the Mediator? Developers end up writing Views and Mediators with poor separation of concerns, and inevitably overflow View logic into their Mediators. Mediators aren’t seen as reusable bits of business logic, they’re seen as an extension to the UI component.
  2. MediatorMap is a type invariant mapping. The map creates a Mediator only if the view component is an exact instance of a mapped type.

    For example, if you register the MyViewComponentMediator type for MyViewComponent, subclasses of MyViewComponent (say, MyViewComponentSubclass) don’t get an instance of MyViewComponentMediator automatically created for them.

    If MyViewComponent encapsulates enough useful functionality to supplement it with its own Mediator, doesn’t MyViewComponentSubclass still implement (through inheritance) and require the same functionality from the MediatorMap? Sure, the subclass may override certain functions, but that’s just polymorphism at work.

Covariant Auto Mediation

The idea behind covariant mediation is that a Mediator is created if a component extends or implements a mapped view component type.

Here’s the practical difference between an invariant check and a covariant check:

//Invariant check:
if(myViewComponent.constructor == MyViewComponent){
    //Do something
}
 
//Covariant check:
if(myViewComponent is MyViewComponent){
    //Do something else
}

If the instance of myViewComponent is actually a subclass of the MyViewComponent type, the first check will fail, but the second check will pass.

Extends or Implements

Extending classes for functionality is all well and good, but it’s not as flexible or as clean as implementing an interface. For starters, AS3 allows you only one parent class, so you can’t compose much functionality through inheritance before running into the diamond problem.

Before I go any further, yes, this means you can map a Mediator for all instances or subclasses of Sprite or UIComponent.

// An instance of SpriteMediator will be created each time an instance
// or subclass of Sprite is added to the display list. Crazy, huh?
map.mapMediator(Sprite, SpriteMediator);

Naturally this introduces performance problems, so everything from the “flash.*” and “mx.*” packages are filtered out by default.

map.registerPackageFilter('flash.*');
map.registerPackageFilter('mx.*');

And you can add your own package filter definitions (like if you’ve got a particle emitter and want to skip inspection of each particle):

map.registerPackageFilter('com.myApp.particles.*');

Anyway, covariantly auto-mediating base classes is alright I guess, but who wants to maintain a complex inheritance tree? Not me. The real sexy magic happens when you start mapping Interfaces for auto-mediation.

Interfaces are covariantly checked:

trace(myComponent.constructor == IMyInterface); //will always be false
trace(myComponent is IMyInterface); //can evaluate to true

Demo Time

This post has been highly theoretical so far, and if you let me continue extolling the virtues of designing interfaces to apply behaviors to components via covariant mediation it will only devolve into something more abstract and academic.

So instead, I’ll show off some code to do it for me. Here’s the demo app I created for my FATC talk.

This demo has three progressively more impressive examples of common problems solved in very few LOC using covariant mediation:

  1. An example of handling system-wide error events.
  2. An example of managing selected screen state.
  3. An example DataGrid where each cell is representative of a unique and open streaming channel from the server.

Rundown

I’ll run through just the meaty parts of this demo. The source for the whole thing is here.

App.mxml and IoC Mappings

Here’s the important part of the Application MXML file:

override protected function createChildren():void
{
	// Normally these IoC mappings are done inside the Context.
	// Do them here because we have access to our children.
	var context:FATC_Context = new FATC_Context(systemManager as DisplayObjectContainer);
 
	var map:IVariantMediatorMap = context.variantMap;
 
	// Map implementations of ISystemErrorUI to have
	// SystemErrorUIMediators registered for them automatically
	map.mapMediator(ISystemErrorUI, SystemErrorUIMediator);
	// Map implementations of IScreen to have ScreenMediators
	// registered for them automatically
	map.mapMediator(IScreen, ScreenMediator);
	// Map implementations of IStreamingServiceUI to have
	// StreamingServiceUIMediator registered for them automatically
	map.mapMediator(IStreamingServiceUI, StreamingServiceUIMediator);
 
	var injector:IInjector = context.theInjector;
	// Tell the injector to create and inject a new instance of
	// StreamingService each time it sees a dependency on IStreamingService
	injector.mapClass(IStreamingService, StreamingService);
 
	super.createChildren();
 
	// Register the mediators for the Screens class immediately.
	// (normally the registration is deferred to the next frame).
	map.registerMediators(screens);
}

ISystemErrorUI and the SystemErrorUIMediator

public interface ISystemErrorUI
{
	function handleError(error:SystemErrorEvent):void;
 
	function get errorAcknowledged():ISignal;
}

Components that are interested in being notified when errors occur can implement the ISystemErrorUI interface. Whenever an error happens in the Application, it’s the responsibility of the errored operation to dispatch a SystemErrorEvent, either bubbling on the display list, or on robotlegs’ shared eventBus. Each instance of SystemErrorUIMediator will notify its ISystemErrorUI instance.

IScreen and the ScreenMediator

public interface IScreen
{
	function set selectedScreen(screen:IScreen):void;
 
	function get screenChangedSignal():ScreenChangedSignal;
}

Implementations of IScreen will be notified when the selectedScreen changes. Implementations of IScreen can cause the screen to change by dispatching their screenChangedSignal member.

Whether the implementor of IScreen truly wishes to change the selectedScreen, or whether it’s simply interested when the selectedScreen is changed, the ScreenMediator doesn’t care. In the demo, three of the four implementations of IScreen respond uniquely when their selectedScreen setter is called:

  1. The Screens class is a ViewStack. When its selectedScreen setter is called, it checks first whether the IScreen instance is a child of itself. If not, Screens adds it as a child. Screens ultimately changes its selectedChild property to the new IScreen.
  2. The ErrorNotificationView class is the bar at the top that drops down when an error occurs. If the bar is showing and the selectedScreen setter is called, the bar hides itself.
  3. The StreamingServiceItemRenderer class is the item renderer for the DataGrid. When the selectedScreen changes to and from the GridScreen instance, StreamingServiceItemRenderer starts or stops the incoming data stream. In this way, we can shut down any data sources for screens that aren’t currently visible.

IStreamingServiceUI and the StreamingServiceUIMediator

public interface IStreamingServiceUI
{
	function setStreamData(value:StreamData):void;
 
	function get updateStreamInfo():ChangeStreamInfoSignal;
}

There’s only one implementation of IStreamingServiceUI: StreamingServiceItemRenderer. StreamingServiceItemRenderer is the item renderer for each cell in the DataGrid.

StreamingServiceItemRenderer takes advantage of the fact that the DataGrid reuses its itemRenderers. When the user scrolls, the DataGrid sets in a new value for data. When that happens, the itemRenderer dispatches its updateStreamInfo member Signal.

When the UI dispatches its updateStreamInfo Signal, StreamingServiceUIMediator tells the service instance the new channel name. Then, the service closes its current channel, and opens a stream with the new channel name.

Because the DataGrid reuses its itemRenderers, and the itemRenderers dispatch changes in its data values to its Mediator, the Mediator can reuse its service instance, which manages opening and closing streams.

This means we only keep open connections for visible cells.

Pretty cool, huh?

Tags: , , ,

The Flex Framework and Modularity: A Manifesto
by Paul Taylor, Jan. 20, 2010, under [ actionscript, bitching ]

If you haven’t watched Greg Burch’s excellent presentation on Slider at RIAdventure, you freakin should. Seriously, stop right now and watch it. But don’t forget about this blog post. Make sure to come back here after you’re done.

The Soul of Flex
Ok, from this point on I am going to assume you’ve done exactly as I told you and watched Greg Burch’s excellent presentation on Slider at RIAdventure. Around 6 minutes in, Greg starts talking about the Soul of Flex. I didn’t have much time to consider it, but two things immediately came to mind: invalidation and styles. In the video you’ll hear me mention CSS but later agree with Greg that CSS or not, styles is the true feature.

Of course, I agree with all the others he said were also part of the Soul: MXML, databinding, states, skinning, item renderers, containers, and components. But as I watched him remove items that weren’t part of the Soul of Flex, I thought to myself:

“Self, this list is completely subjective and based entirely on my opinion. It is extremely likely that someone else’s definition of the Soul of Flex is different than mine, and who’s to say which one is right? Is it Adobe’s job? Now that they’ve packed a framework full of features that real people use, they’re going to remove a good many features simply because the they do not fall into Adobe’s definition of what the Soul is? That’s not right. The community is too diverse for that. Flex is the Soul of Flex.”

The real Soul of Flex

I say Flex is the Soul of Flex.

This got me thinking about Flex with regards to mobile development. At the time of this writing, every Flex 4 component has a base class that is 13,246 lines of code (1412 blank lines, 6423 comment lines, and 5411 actual code lines.) If you’re using Flex 4 and skinning, you have two components, so multiply potentially everything the UIComponent does by 2:

  • 2x the UIComponents to initialize
  • 2x the UIComponents added to the inheriting style chain
  • 2x the UIComponents to make validation passes on
  • 2x the event listeners that are registered
  • 2x the number of objects for the DragProxy (in the DragManager) to find in its getObjectsUnderPoint() implementation
  • and on and on…

This means one of two things. Either the decision to abstract skins into another UIComponent was monumentally retarded, or the UIComponent is too large and tries to do to many things. Since I believe the new skins are wonderful from an architectural/modularity standpoint, I have to pick the second choice: the UIComponent is too damn big. It tries to do too damn much. Let me reiterate: I love the new Spark skins. I hate the size of the UIComponent.

The Question of Responsibility
Why is the UIComponent (at the time of this writing) 13,246 lines long? The answer is features. The UIComponent violates the Single Responsibility Principle. Here’s a list of most of the features the UIComponent encapsulates:

  • Invalidation – Implements IInvalidating.
    • Defines invalidate properties/size/displayList functions.
    • Defines validateNow function.
  • Validation – Implements ILayoutManagerClient.
    • Defines the initialized, nestLevel, processedDescriptors, and updateCompletePendingFlag properties.
    • Defines validate properties/size/displayList functions.
  • Styles – Implements IAdvancedStyleClient, the only visual class that does.
  • States – Implements IStateClient and IStateClient2 to support the new Fx4 states syntax.
  • Tooltips – Implements IToolTipManagerClient, the only component class that does.
  • Constraint-based layout.
  • IRepeaterClient – UIComponents (and subclasses) can be created by Repeaters.
  • Databinding.
  • Related to Databinding, implements IPropertyChangeNotifier.
  • Embedded fonts.
  • Focus – Doesn’t implement IFocusManagerComponent by name, but does implement the functions. Has a reference to the FocusManager and a focusPane property.
  • Validators – Implements IValidatorListener, which allows the UIComponent to respond to ValidationResultEvents dispatched by the Validator classes.
  • Modules – Implements IFlexModule, so if created by an IFlexModuleFactory, the factory stores a reference of itself on the UIComponent.
  • Explicit/measured- max/min – widths/heights.
  • Percent widths/heights.
  • Enabled/disabled.
  • Effects.
  • Special logic for adding/removing children.
  • All the events it creates and dispatches, including
    1. Initialization events, like preInit and creationComplete.
    2. FlexEvents like show, hide, move, resize
    3. StateChangeEvents.
    4. DragEvents for the List classes. Not part of the public API.
    5. ToolTipEvents
    6. FlexMouseEvents

Man, the UIComponent does a ton of stuff. We haven’t even gotten into the measurement, layout, containment, children handling, skinning, item renderers, or graphics functionality of Flex yet!

The UIComponent is the base class for… everything
At least, everything visual. Not including Flex 3 skins. It’s the base class for all non-Fx3-skin-related visual elements.

The UIComponent has so many responsibilities because it is the base class for so many controls. You want a Label or TextArea? They’re UIComponents. You want a Button? It’s a UIComponent. You want a container of Buttons? It’s a UIComponent.

But there are a few things I can’t figure out about this configuration:

  1. Why does a container need logic in its base class that accesses embedded fonts, or creates UITextFields from embedded font contexts (I checked, UITextField is the only class passed into createInFontContext()).
  2. Similarly, why does a Label require any knowledge about processedDescriptors, states, or validators? Labels can’t have children, states, or validators. In fact, Labels can’t do much except display text. Why do they need to be IFlexModule objects? I could go on, but I think you get the point.

There is a trend of WTFs like this about the UIComponent that can only be explained by, “it’s the base class and we’re trying to keep our API super clean.”

There is a better way: Composited Modularity
Most of the functionality is already segregated by the liberal use of interfaces… now lets actually implement it that way.

Almost all the functionality that the UIComponent (and subclasses of UIComponent for that matter) contains can be grouped into smaller, more discreet classes. Lets call them modules. Once this is done, the UIComponent simply exists to provide a unified API to the developer and glue with which to assemble the modules. More complex controls and components are simply composed of more complex modules.

For example, the functionality for embedded fonts should be isolated into its own module. Then, only the Label, Text, TextArea, and maybe some other controls that require direct access to embedded fonts need to include the “EmbeddedFontsModule.” This way, the VBox doesn’t have to include functionality for accessing embedded fonts.

Similarly, a VBox would include special modules for adding children, measurement, and layout. See, this is easy.

And while we’re at it, let’s rewrite the LayoutManager.

You heard me. The LayoutManager. It’s in my sights.
What’s so special about 3-phased validation that it has to be hard-coded into the Flex framework? Is it phase ordering? Nest-level ordering? That’s not special. That’s algorithms.

First, for those who don’t know, the LayoutManager is what enables Flex components to do the awesome 3-phase component lifecycle that we’ve all come to know and love. When a component is invalidated for a phase (say, invalidateProperties()), he registers with the LayoutManager. The LayoutManager adds the component to the proper invalidation queue. There are 3 invalidation queues, one for each validation phase. The invalidation queues are a special PriorityQueue implementation. This is important, because there is an order to the validation process.

Components have a nestLevel, which is really just their position in the display list. The Application’s nestLevel is 3. The nestLevel increases from there, so the lowest component in the display tree has the highest number.

When a component is added to the invalidatePropertiesQueue, it is added at its nestLevel priority. When the invalidatePropertiesQueue is processed, components with the lowest nestLevel, the ones closest to 0, are processed first. This is called “top-down” processing, because it starts at the top of the display list and processes to the bottom.

The commit phase does top-down processing, because generally parents can commit properties on themselves and change properties on their children, which will then get committed. The measure phase is from the bottom-up (can you guess what that means? If not, put it in the comments.), because a parent will often make a decision on his size based on the sizes of his children. The update phase is another top-down queue, because now that the sizes and positions are calculated, it’s time to lay them out. This is what makes percent widths/heights, scrollbars, etc. possible.

Rethinking validation
Does validation have to be hardcoded? Are the 3 phases all that are needed? How hard would it be to switch to just 2? 4? 100?

The fact is, the developer should be able to hook into the power of phased updates for himself. Why have a manager that only validates 3 hard-coded phases, when you could have a manager that validates an unlimited number of dynamic phases?
Well sure Paul, that sounds great and all, but it’s kind of a tough problem. <– Shut up alternate text, I’ve got a solution.

Keep the idea of nestLevel. It’s clear, clean, and definite. Change the idea of 3 hard-coded phases into an unlimited number of injected phases, based on priorities. Similar to nestLevel for components, priorities are arbitrarily defined by the developer. A priority is an Array of ints. It can be as simple as “1″ or as complex as “1.2.3.4.” It is only used as a basis for comparison between other priorities. For example, priority 1.2 is greater than priority 1.2.1, since it is assumed 1.2.1 is a subset of 1.2. However, 1.200 is much greater than 1.2, because 200 > 2. Do priorities make sense? If the answer is no, put it in the comments.

The idea is that a developer can invalidate a component for a certain phase. The phase has a priority (like 1.3), and a direction (UP or DOWN). The ValidationManager keeps a Heap of Heaps, sorted by priority. If it doesn’t find a Heap at the priority specified, it creates a new Heap, adds the IValidationClient to it, sets the proper sort direction (UP or DOWN) on it, then adds the new Heap to the Heap of Heaps. If it does find a Heap, it adds the IValidationClient to the Heap that it found.

When it comes time to Validate (on the next enter_frame event), the ValidationManager dequeues each Heap from the Heap of Heaps. Then it dequeues each item from each individual Heap, validating it as it goes.

The beauty of this is that the Flex component lifecycle can be perfectly mimicked. Set the three phases to happen one after another, and you can get the same results. For APIs sake, lets add a gap between them, so a third party developer can come by later and inject his own phases between the 3 usual phases. If we set commit at 1.3, measure at 1.5, and update at 1.7, this should give us enough of a gap for developers to use.

Say Developer X wants to add some extra umph to his custom component, but needs this processing needs to happen in-phase and inbetween the measure and update phases. He simply needs to inject his own validation phase anywhere between measure at 1.5 and update at 1.7, lets keep it simple and say 1.6, and BAM. ValidationManager will validate his phase right after measure and right before update.

This allows the developers a whole new avenue of development, something that they would have had to tack onto one of Flex’s 3 hard-coded phases before.

This is all words. Where are the actions. You fail.

This is where I introduce FlashWorks
FlashWorks is a component set for Flash and FlashBuilder, designed around the concepts of a tiny core API and modularity.

It includes the ValidationManager and validation scheme I’ve outlined here, as well as a few other things:

  • MXML development
  • Skinning similar to Flex 4
  • Graphics primitives (extend EventDispatcher)
  • Measurement and Layout controllers with support for percent widths/heights
  • Injectable Validation phases
  • States, including support for FB4′s new States syntax
  • Styles, through a controller that interfaces with Jesse Freeman’s F*CSS
  • Databinding
  • SystemManager that works with FB4′s code generation, so the FlashWorks movie is 2 frames, just like Flex.
  • A modular, pay-as-you-go system, so functionality is only included when it’s used. For instance, the States controller isn’t included unless States are defined on the component.

Alright, here’s the code and demos

[Edit] I’ve since moved from this project onto Reflex. I feel it has more of a chance for success than a solo framework by me would.
I’m keeping the demo and demo code here as a proof of concept:
Demo: http://guyinthechair.com/misc/flashworks/FlashWorks.html
Source code for demo: http://guyinthechair.com/misc/flashworks/srcview/index.html

Tags: , , , ,