HowTo ActionScript 3 (as3 flash or flex or AIR) Class and Object Inheritance, extends

It seems I have been getting alot of hits due to my post on AS3 inheritance, and I notice that most of the documentation and examples out there don’t really cover the whys and the hows in a thorough manner.

There are a couple of parts to this little tutorial, the first being a kind of very basic demonstration of extending, and then some concrete realworld samples. You can jump ahead to the realworld samples if you are just looking to see how/why you might use extend to solve some real problems.

Basic/intro to oop and extending in as3 extending event dispatcher to manage your own event queue extending event to create a custom event and finally extending Panel to make a drag/drop Panel


Having classes and objects does not equal OOP.

To understand a class, you need to understand that an object without state or behavior is meaningless, so to be an object, it must have those things. The object itself is an environment, it is like a scope, it’s a container which imparts a kind of semantic meaning to the function and variables attached to it.

Having objects is very nice, but there needs to be something more, and that something more is inheritance. Without inheritance, objects are simply wrappers, and it would be just as well to create functions and simply pass in some kind of key to ameliorate what to do. With inheritance, you share state and behavior between objects.

The common example is with a person, people have names, and birthdays, and they can do things like walk, and talk. Generally speaking, all people can do those things, so Person is what we would call a BaseClass.

Let’s test it out:

Main.mxml


	
		
	

Then we create two classes inside the com/jm/example directory:

package com.jm.example {
	public class Person {
		public var name:String;
		public var birthday:Date;
		public function Person() {
			
		}
		
	}
}

And then we create the subclass:

package com.jm.example {
	/**
	 * ...
	 * @author Jason Martin
	 */
	import com.jm.example.Person;
	public class Girl extends Person {
		
		public function Girl (name:String, dob:String) {
			this.name = name;
			this.birthday = dob;
		}
		
	}
}

Here we have simple inheritance via the extends keyword. Essentially what this means is: “copy all public instance variables and methods from Person to Girl.”

Action Script 3 is a prototype oriented language, meaning that there is a base object, called Object. When you create a new object, it is entered into the stream by “cloning” Object. When you create a subclass, like Girl, the BaseClass Person is “cloned” to create the current “instance” of Girl.

The function that we use, describeType, which is in the mx.utils package, simply writes out the object data in xml format. In the Main.mxml file, we have a simple textarea that we use to display the text.


  
  
  
    
    
  
  
  

You’ll notice that there are two extendsClass tags, one is for our base class, Person, and one for object.

Now, let’s go a bit deeper, we’ll make a few changes, and see how things are effected, we’ll also introduce a new keyword, called super, which essentially defers to the Base Classes “constructor”.

package com.jm.example {
	import com.jm.example.Girl;
	public class LittleGirl extends Girl{
		
		public function LittleGirl(name:String, dob:String) { //this function is called the constructor
			super(name, dob); //this defers to the Base Class's constructer, i.e. public function Girl(name:String,dob:String) 
		}
		
	}
}

Constructors are named after the class, and also they have no return type defined. That is because they are special methods. They always return themselves, or more precisely, they return the instance of the class.

The super keyword allows you to pass some of the arguments up the inheritance “hierarchy” to make use of the base class’s constructor logic. As a normal rule, unless it’s really necessary or convienent, you shouldn’t put too much logic in the constructor as it makes the object instance inflexible. You should also never instantiate classes inside of a constructor. The reasons why are a bit more complex than this introductory post, but I will explain it anyway, as it’s good to get a start on the right foot.

When you instantiate objects inside of a constructor, it is easy to lose track of where/when you are instantiating, and it makes testing a complete bitch. The easiest way to explain this is through the design philosophy of Dependency Injection, which comes from Martin Fowler I believe (of active record fame, get his book, it’s excellent). Essentially, you can break objects into consumers and providers. Like a grocery store, when a person goes to a grocery store, they are provided with a shopping cart, they buy or are given a purse, or wallet, the store provides the cash register and so on. Instantiating objects inside a consuming class is like a person bringing their own cash register and setting it up at the counter just to buy some cheese. You never assemble or aggregate any data within the class that will consume it. If you have a database, create a class whose job it is to assemble/find/update that data and provide it in a sufficiently abstract container, like an object or an array or arraycollection etc.

The main reason for this is testing. Maybe you normally get your data from a database, but for a unit test, you want to pull it from an XML file. Or maybe normally you connect via an AMF gateway to query a remote database, but you want to test against a local database. You should be able to switch out a single class/object/component/module for another without the consuming class even realizing it’s happened. There are a few different types of dependency injection, the two main and most useful ones are “constructor injection” and “setter injection”, that is, dependencies (objects) are either passed in through the constructor, or they are passed to an instance method to be set.

This really isn’t the time or place to go further into DI, but I’ll cover it in a later post for those who are interested.

Now back to our regular scheduled topic.

Here, we’ve created a class called LittleGirl, which we will test in the same manner. We change the Main.mxml init function like this:

  ...
  mainText.text = describeType(new LittleGirl('Jane',new Date(Date.parse('01/06/1983'))));
  ...

Then we look at the ouput from describeType:


  
  
  
  
    
    
  
  
  

You’ll notice that Girl, Person, and then Object are in the extendsClass chain.

You’ll also notice that you have some constructor information, the index attribute indicates the order they are to be passed in. With this information, you could do a lot, especially when using a Factory to generate instances from say a database. The other information indicates what type and names are present as attributes or instance variables. In this case, name and birthday.

Now, let’s modify it even further, and add a method in there (You’ll notice by now that there are many different words used to describe the same thing, I try to use them all so that you can get a feel for the lingo. Methods, Functions, Instance Methods, Behaviors, Slots, Queries etc, all mean the same thing, Functions, but specifically functions on a class/object).

What we will do, is add a method to the Person class, and see what happens when we try to call it from LittleGirl.

   ...
   public function sayHello():String {
			var now:Date = new Date();
			var years:int = Math.floor((now.valueOf() / 1000 / 60 / 60 / 24 / 365) - (this.birthday.valueOf() / 1000 / 60 / 60 / 24 / 365));
			return "Hello, my name is " + this.name + " and I am " + years + " years old! I was born on " + this.birthday.toDateString();
   }
   ...

And now the output:


  
  
  
  
    
    
  
  
  
  


Hello, my name is Jane and I am 27 years old! I was born on Thu Jan 6 1983

Well, 27, not so little huh. I was at a loss for a Birthdate and used my own. I always said if I had a daughter, I’d hope she was Capricorn like her old man. Goats FTW.

So these are the major uses of the extends keyword, and what it means to Sub-classed objects.

The next thing to consider is: static functions. Static functions are not copied from the base class. If you try, you will get an error like: ” Error: Call to a possibly undefined method “method name here” through a reference with static type Class.” Static properties, or variables will also not be copied.

I am sure by now you have the general idea, the question is, now that you know how to extend, what the hell do you do with it.

The simple answer is: You are going to be extending alot, from two main classes. EventDispatcher and Event.

No matter what application you are creating, you will also always want to handle your own events and create custom events. Here’s an example, where we extend from EventDispatcher, and create a Singleton manager class, and a custom even class.

 


	
		
	

package com.jm.example {
	import flash.errors.IllegalOperationError;
	import flash.events.EventDispatcher;
	import com.jm.example.EventExtended
	public class EventDispatcherExtended extends EventDispatcher{
		public var events:Array = new Array();
		public static var _self:EventDispatcherExtended;
		public function EventDispatcherExtended() {
			if (_self) {
				throw new IllegalOperationError("You can only instantiate 1 instance of EventDispatcherExtended");
			}
		}
		public static function getSelf():EventDispatcherExtended {
			if (!_self)
				_self = new EventDispatcherExtended();
			return _self;
		}
		
		public static function add(event:String, f:Function):void {
			EventDispatcherExtended.getSelf().events.push( { event: event, method: f } );
			EventDispatcherExtended.getSelf().addEventListener(event, f);
		}
		public static function remove(event:String, f:Function = null):void {
			for (var i:int = 0; i < EventDispatcherExtended.getSelf().events.length; i++){
				if (event == EventDispatcherExtended.getSelf().events[i].event) {
					if (f == null) {
						if (f == EventDispatcherExtended.getSelf().events[i].method) {
							EventDispatcherExtended.getSelf().removeEventListener(event, f);
						}
					} else {
						EventDispatcherExtended.getSelf().removeEventListener(event, EventDispatcherExtended.getSelf().events[i].method);
					}
				}
			}
		}
		public static function send(event:String, data:*):void {
			EventDispatcherExtended.getSelf().dispatchEvent(new EventExtended(event, data));
		}
	}
}


Then the EventDispatcherExtended class:
And finally, we extend from Event to create our own custom event class:

package com.jm.example {
	import flash.events.Event;
	public class EventExtended extends Event{
		public var data:*;
		public function EventExtended(event:String, data:*, bubbles:Boolean = true, cancelable:Boolean = false ) {
			super(event, bubbles, cancelable);
			this.data = data;
		}
	}
}


While that's all fine and good, in the above we are extending to replace the parent. That is, we don't want the normal way of dispatching events, we want them all managed and centralized, so we extend to replace. Another type of extending is to enhance. Let's use extend to create a draggable panel.



	
		
	

And then the actionscript class file like this:

package com.jm.example {
	import flash.events.Event;
	import mx.containers.Panel;
	import flash.events.MouseEvent;
	public class DraggablePanel extends Panel {
		public function DraggablePanel() {
			super();
			addEventListener(MouseEvent.MOUSE_DOWN, drag);
			addEventListener(Event.ADDED_TO_STAGE, stageAttach);
			
		}
		public function stageAttach(event:Event):void {
			stage.addEventListener(MouseEvent.MOUSE_UP, drop);
		}
		public function drag(event:MouseEvent):void {
			parent.setChildIndex(this, this.parent.numChildren - 1);
			startDrag();
		}
		public function drop(event:MouseEvent):void {
			stopDrag();
		}
	}
}

A few things to note is: startDrag and stopDrag are part of the panel class, it's an already inherited behavior. We are really just adding in a few help functions to get the ball rolling here. Another note is in the constructor, we attach an event ADDED_TO_STAGE, it's only after we receive that message that we access the stage object, because there are alot of pre-initialize steps that go on before the stage is actually created or the stage property is not actually set until the item has been ADDED to the stage, which happens after the constructor is called, this helps to avoid some null object reference errors.

About Jason

I am a 26 year old programmer living in the south of France. I currently work actively in the fields of Ruby/PHP/Javascript and server/website administration.
This entry was posted in ActionScript 3 Tutorials, Flash/Flex, Flex/AIR Tutorials, OOP, Topics, Tutorials, Web Design and tagged , , , . Bookmark the permalink.