After a few weeks of working with the AbstractComponent class I think it's time for it's graduation. I've made quite a few changes since the version I posted back in June. These have mostly been to simplify the class so I'll run through some of the thinking first then post the code for the first time as a downloadable class.

AbsractComponent is now BaseComponent
Okay I've been mulling this one for a few weeks now but I'm going to change the name of the base class from AbstractComponent to BaseComponent. I know it seems like a little thing but I think BaseComponent is more descriptive. It also leaves the way open for an AdvancedComponent (possibly with child management and granular invalidation) at a later date.

suspend() and resume() are gone
Although I still like the idea of being able to suspend and resume a component whether it is on stage or off I found that in practice it led to over complicated code. So suspend() and resume() are gone and there function is now absorbed into the existing init() and destroy() functions. Just to recap:

init()
This function is automatically called when the component is added to the stage either by placing it on the timeline or by addChild() in AS3. You should place all your initialisation code here.

destroy()
This function is automatically called when the component is removed from the stage either by removing it from the timeline or by removeChild() in AS3. You should place all your suspend and garbage management code here.

I have reworked the functions that call init() and destroy() so that you no longer need to call super.init() or super.destroy() when you directly extend the BaseComponent class.

Garbage Management
Garbage Collection continues to be a real hot topic in the Flash development community. Last month Colin Moock posted an unofficial list of activities that need to be carried out in order to release a SWF or MovieClip for garbage collection. One of the aims of my BaseComponent class is to provide a simple standard structure and then automate the process where possible. If you didn't catch the post then here's the list in full:

  • Tell any loaded .swf child assets to disable themselves.
  • Stop any sounds from playing.
  • Stop the main timeline, if it is currently playing.
  • Stop any movie clips that are currently playing.
  • Close any connected network objects, such as instances of Loader, URLLoader, Socket, XMLSocket, LocalConnection, NetConnections, and NetStream.
  • Release all references to cameras and microphones.
  • Unregister all event listeners in the .swf (particularly Event.ENTER_FRAME, and mouse and keyboard listeners)
  • Stop any currently running intervals (via clearInterval()).
  • Stop any Timer objects (via the Timer class's instance method stop()).

A new IComponent interface
If you need a formal reminder to add an init() and destroy() function to any class that extends BaseComponent then you can implement the new IComponent Interface.

draw() is now public
I've simplified the invalidation() and draw() code by making draw() public. If you need to redraw something straight away then call draw(). If you want to buffer requests and schedule a redraw then call invalidate().

That's it really. The new class is a lot simpler and cleaner but the startup and shutdown (garbage management functions) are still fully automated. Nice.

Override warnings for init() and destroy()
I've added a couple of trace statements to these functions. If you override the functions in your own class then you'll never see these. They are really there as a warning to remind you to always override these two functions. Thanks to Vic for pointing out that using the interface won't throw any errors - oops!

The Code: BaseComponent.as

package org.computus.core
{
  import flash.events.Event;
  import flash.display.MovieClip;

  public class BaseComponent extends MovieClip
  {
    // ------------------------------------------
    // CONSTRUCTOR
    public function BaseComponent():void
    {
      super()
      // n.b. These events require a minimum Flash Player version of 9.0.28.0
      addEventListener( Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true )
      addEventListener( Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true )
    }

    // ------------------------------------------
    // MEMORY MANAGEMENT
    public function init():void 
    {
      // Warning is raised if subclass doesn not override init()
      throw new Error( "WARNING: " + this + " did not override init()" )

      // initialise component here.
    }

    public function destroy():void
    {
      // Warning is raised if subclass doesn not override destroy()
      throw new Error( "WARNING: " + this + " did not override destroy()" )

      // suspend component here.
    }

    // ------------------------------------------
    // EVENTS
    private function onAddedToStage(e:Event):void
    {
      init()
    }

    private function onRemovedFromStage(e:Event):void
    {
      stop()
      removeEventListener(Event.ENTER_FRAME, onInvalidate)
      removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage)
      removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage)
      destroy()
    }

    private function onInvalidate(e:Event):void
    {
      removeEventListener(Event.ENTER_FRAME, onInvalidate); 
      draw(); 
    }

    // ------------------------------------------
    // DRAW
    protected function invalidate():void
    {
      addEventListener(Event.ENTER_FRAME, onInvalidate, false, 0, true);
    }

    public function draw():void
    {
      // redraw component state here
    }
  }
}

The Code: IComponent.as

package org.computus.core 
{
  public interface IComponent
  {
    function init():void function destroy():void
  }
}