The Instantiator: A simple Flex utility class for more powerful skinning in custom components

One thing that bugs me about the core Flex SDK components is that they use the Class datatype for just about everything. From time to time, you encounter the awesomely flexible mx.core.IFactory type for a renderer property, but when it comes to styles, Class is king. The problem is that sometimes I wish I could instantiate a Function object prototype-style. It’s a great way to create new “classes” at runtime.

There’s nothing I can do to fix this limitation in the core components without patching the source manually (I know, I’ve tried several crazy ideas), but when I create a custom component, I can make it much more flexible. The easiest solution, the one I’ve been using most often, is to simply type the style value as Object whenever I use getStyle() for a skin style:

var skinClass:Object = this.getStyle( "skin" );
var skin:DisplayObject = new skinClass();

This allows Function to be used with the new keyword like a Class, which is generally enough for my needs.

However, I also mentioned IFactory earlier. There’s something compelling to me, as a component developer, in the simplicity and power of allowing my developers to use a factory to customize skins. I usually find a decent enough substitute through styleName or the StyleProxy class, but I feel like I’m rewriting the same boring code over and over again whenever I make yet another skin or sub-component styleName style. Don’t get me started on chaining styleNames multiple levels deep. Every time I do it, it feels awesome because I know everything is super-customizable, but I also wonder how many developers understand how these work. Of course, the fact that you can’t create an IFactory in your stylesheets is a problem. One could argue the same thing about using a Function to instantiate a skin, though, but I find a function’s ability to create a runtime datatype through prototype too compelling to ignore. I’m not quite as sure about IFactory, but I feel the option is useful to have, even if I don’t need it too often.

I could always change my instantiation code to look like the following, but I’d hate to repeat these several lines over and over again when I want to build a skinnable component:

var skinBuilder:Object = this.getStyle( "skin" );
var skin:DisplayObject;
if( skinBuilder is IFactory )
{
    skin = IFactory(skinBuilder).newInstance();
}
else
{
    skin = new skinClass();
}

With that much code needing to be repeated across multiple components, it’s starting to scream utility function. A few minutes ago, I came up with the idea for The Instantiator a useful utility for just this situation. Check it out:

var skinBuilder:Object = this.getStyle( "skin" );
var skin:DisplayObject = TheInstantiator.newInstance( skinBuilder );

Basically, you pass in the raw style value, and TheInstantiator will create a new instance if it knows how. If not, then it returns null. Here’s the source for the class:

////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2008 Josh Tynjala
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to
//  deal in the Software without restriction, including without limitation the
//  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
//  sell copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
//  IN THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package com.flextoolbox.utils
{
	import flash.utils.getDefinitionByName;

	import mx.core.IFactory;
	import mx.styles.ISimpleStyleClient;

	/**
	 * Utility for creating instances of Flex sub-components or skins.
	 *
	 * @author Josh Tynjala (joshblog.net)
	 */
	public class TheInstantiator
	{
		/**
		 * Creates a new instance of an object. Supports Class, Function, a
		 * fully-qualified class name String, or mx.core.IFactory.
		 *
		 * @param type			The type to instantiate
		 * @param styleName		The styleName to set if the instantiated object supports styles.
		 */
		public static function newInstance(type:Object, styleName:String = null):Object
		{
			var instance:Object = null;

			//first check whether type is a String.
			//if so, try to convert to a class
			if(type is String)
			{
				try
				{
					//will throw an error if it's not a fully qualified class name
					type = getDefinitionByName(type as String);
				}
				catch(error:Error)
				{
					return null;
				}
			}

			//Class and Function may be used with the new keyword
			if(type is Class || type is Function)
			{
				instance = new type();
			}
			else if(type is IFactory)
			{
				//mmmmm... factories are great
				instance = IFactory(type).newInstance();
			}

			//if the new instance supports styles, set the styleName property.
			if(instance is ISimpleStyleClient)
			{
				ISimpleStyleClient(instance).styleName = styleName;
			}

			return instance;
		}
	}
}

You’ll also notice a couple extras. First, you can also pass in a fully-qualified class name as a String value, and it will automatically retrieve the proper class to use, or it will return null if it fails to find the class. Second, I defined an optional styleName argument. Since setting style names is often part of creating a new instance of a skin or a sub-component, it’s easy to get this done at the same time and save a couple lines of code. There’s a check for whether the instantiated object is an ISimpleStyleClient, which is Flex’s interface for display objects that support styleName, so nothing should go wrong if the object doesn’t support styles.

You know, reading this all over again now, I think it would be great if ActionScript defined an interface for datatypes that could be instantiated with the new keyword. Class, Function, and even Flex’s IFactory could implement it, and developers could create their own factory-like classes that could be used in place of Class with the new keyword. That almost smells like a feature request….

About Josh Tynjala

Josh Tynjala is a frontend developer, open source contributor, bowler hat enthusiast, and karaoke addict. You might be familiar with his project, Feathers UI, an open source user interface library for Starling Framework that is included in the Adobe Gaming SDK.