Home >
Interfaces and Dynamic Class Instantiation
I always enjoy Jesse Freeman's Flash Art of War column, and this week's, "Dynamically Creating(/Instantiating) Classes from XML," was especially intriguing. The thing that stuck out to me about that post is that if you are creating objects dymanically, you don't know what properties and methods are going to be available to you.
The way that Jesse handled this is that he created his class that he was instantiating based on the XML (RedBox) to extend Sprite and typed his instance variable as DisplayObject. Even though the RedBox class exposes a public method, redraw(), his document class never calls it and, in fact, cannot call it without casting the DisplayObject back to RedBox type. When you're only positioning things on screen and sizing them, the default properties on DisplayObject work great.
But one of the commenters on that article, "joshspoon," talked about a project where he has art objects in a series of rooms. The idea that you have rooms with objects in it is a fairly common problem, and potentially involves more complicated concepts than just position and size.
Say, for instance, you have a game which needs a Gallery class that only allows objects to be positioned along walls, and they all have to be Art objects, and a Tavern class that can have Tables, Chairs, and exactly one Bar. And both a Gallery and Tavern will have Doors.
So how do we handle this? One way we could do this is to create a Room Class and then create Tavern and Gallery as subclasses that override its default method implementations. So say we decide to make Room a subclass of MovieClip, because our first two Room types are using the timeline. But then what happens if we need a Cave that doesn't need timeline support? Well I suppose it doesn't hurt (much) to have extra functionality we don't need, but wouldn't it be nice if we can choose to make Cave extend Sprite if that's what we need?
Enter Interfaces. An Interface is like a contract with an object. The object says "I implement Interface X, so I will have property a, b, and c, and methods y and z." Because of that, any other class that has to interact with an object can safely read those properties and call those methods, regardless of what the base class is.
So, for instance, if IRoom looks like:
package
{
import flash.display.DisplayObject;
public interface IRoom
{
//properties
public function get childObjects():Array;
public function get entrances():Array;
public function get childObjectErrors():Array;
//methods
public function addChildObject(object:DisplayObject):DisplayObject;
public function removeChildObject(object:DisplayObject):DisplayObject;
public function addEntrance(object:DisplayObject):DisplayObject;
public function removeEntrance(object:DisplayObject):DisplayObject;
}
}
Then anything that implements IRoom will have methods to manage the objects in them and their entrances. In addition, they have read only properties that allow consuming classes to enumerate their entrances and child objects and to check and see if all of the children are there that should be there.
Each Room class can choose how to implement those properties and methods. For example, Gallery may choose to throw an error or pop up an alert if the XML engine tries to call its addChildObject with something that's not an Art object. Since DisplayObject already has x, y, height, and width properties, it can choose to throw an error if the combination of these does not result in wall placement, or it can choose to simply move the objects to the nearest wall. It could also save any errors of this type until childObjectErrors is called.
Tavern can make similar choices, but since Tavern must contain exactly one Bar object, it doesn't make sense to try to throw errors or pop up an alert every time a child is added when this condition is not met. Instead, the engine instantiating the IRooms and their childObjects will need to check childObjectErrors once it has looped through all of the provided XML.
Because the Interface doesn't force the implementation, the classes are able to do what makes most sense in the context of their own functionality. By the same token, the Document class that is setting these objects up without knowing specifically what it's creating can confidently add objects and entrances and get information about these later, all without having to know whether it is looking at a Gallery, a Tavern, or a Cave.




Facebook Application Development
OH NO, you used the word Dynamic. Hope you don't get the same "love" I got over my use of the word in the title. Great post and you stole a little of my thunder for a follow up but I'm happy to see others talking about interfaces. I can't stress enough how interfaces help in situations like this! This also highlights the ban of my existence as an AS 3 developer, the missing IDisplayObject interface. When I create my classes on the fly I usually type them directly to DisplayObject so I can do layout and "easily" add them to a display list. Once I have them created I usually store them in an array or look-up table of sorts. When I need to directly manipulate an instance I request it from the array and use an interface so I can it's properties based on the situation it is in. This is how I get around a lot of AS 3's typing issues. Don't get me wrong, I like that AS 3 is strongly typed, but it gets in the way of instantiating (not dynamically) creating classes on the fly and calling methods on them.
This is such a deep topic and one I doubt I will ever have time to fully dive into but I use this everyday in the Flash applications I build. Thanks for continuing the conversation and I hope this inspires others to play around with the idea. Also, make sure you use interfaces where ever they make sense ( especially since there is a small performance hit for using to much of a good thing ).
I'm sorry to steal your thunder, but I'm sure that your post will be just fantastic. And I think it helps people trying to learn this stuff to see different peoples' perspectives on the same thing.
And now I understand how the topic of Jesse's post can work a little better. I wasn't quite getting it, and was thinking about how unlikely it was for me to use based on the extreme set-up requirements to make it work.
@Jesse - the criticism on your post is not on your use of the word "dynamic" but on the rest of the title. The title implies that you are actually building a new class using XML. Instead, you are only instantiating classes that may or may not have been compiled in to the project. Now, I would be really excited if I could actually create brand new classes on the fly in an XML document, rather than have to create the class, include it in a SWF, load that SWF, and then instantiate it.