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: , , , ,