
This article is not about werewolves or other shape-shifting creatures. Polymorphism is an object-oriented concept. Perhaps THE object-oriented concept. For a language to truly support object-oriented paradigms, it must provide developers with the ability to implement polymorphic objects. Luckily for us, Actionscript 3 meets this criteria. This article is an introduction to polymorphism and how you can utilize the concept to build stronger applications, promote code reuse, and more easily unit test your code.
Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B. from Wikipedia
Polymorphism in Actionscript 3 can be approached from two directions. One approach is to use inheritance. The other is via Interfaces.
Abstract (base) Class Inheritance
Inheritance means to extend a class:
public class MyClass extends SomeClass
MyClass inherits all of the public/protected properties and methods of SomeClass. SomeClass is referred to as the super class of MyClass in this situation. When a class is extended in this fashion, Actionscript provides the ability to override methods of the super class. Only properties and methods that utilize the public or protected namespaces can be overridden. Private properties and methods are inaccessible to the class that is extending the super class.
//Animal.as
package
{
/**
* Abstract animal class.
*/
public class Animal
{
protected var name:String;
public function Animal(name:String)
{
this.name = name;
}
public function getName():String
{
return name;
}
public function talk():void
{
//a concrete animal should be able to make some noise.
}
public function eat():void
{
//a concrete animal should be able to eat food.
}
}
}
Animal is an abstract base class that will provide the basic functionality that all of the animals in our application will use. Each animal has a name, can talk, and can eat food. The abstract animal can't do any of this. It has no voice, no name, and can't even eat to sustain itself. It is the concept of an animal, not an actual animal. To make this concept into something more concrete, we need to extend the Animal class and create an actual animal.
//Dog.as
package
{
/**
* Concrete animal class
*/
public class Dog extends Animal
{
public function Dog()
{
super("Dog");
}
override public function eat() : void
{
trace("nom nom nom dog food");
}
override public function talk() : void
{
trace("woof woof");
}
}
}
//Cat.as
package
{
/**
* Concrete animal class
*/
public class Cat extends Animal
{
public function Cat()
{
super("Cat");
}
override public function eat() : void
{
trace("nom nom nom a mouse");
}
override public function talk() : void
{
trace("meow meow");
}
}
}
The Dog and Cat classes are both concrete examples of Animal classes. They extend the abstract base class, Animal, and take the concept of an animal to provide us with an animal that can actually do things. The concrete Animal classes override the public methods eat and talk so that these animals behave as one might expect them to. It can be noted that it is wholly possible to create an instance of the abstract Animal class. Actionscript does not provide explicit Abstract classes as other languages might. An instance of the Animal class wouldn't be very useful however. It can't do anything! There are several "tricks" that one can utilize to prevent the direct instantiation of "abstract" classes, but that is for another article.
//Polymorphism.as
package
{
import flash.display.Sprite;
public class Polymorphism extends Sprite
{
protected var menagerie:Array = [];
public function Polymorphism()
{
//create som Animal instances
var cat:Animal = new Cat();
var dog:Animal = new Dog();
menagerie = [ cat, dog ];
//make the Animal instances perform
for each(var animal:Animal in menagerie)
{
trace(animal.getName() + " is an Animal")
animal.eat();
animal.talk();
trace("----");
}
}
}
}
The loop in this example is looking for objects that are of the class Animal. Since both Cat and Dog extend Animal, they are Animal objects. Therefore, all of the methods available to Animal, are available to our specific concrete animals via the overridden methods.
Cat is an Animal nom nom nom a mouse meow meow ---- Dog is an Animal nom nom nom dog food woof woof ----
Real World Inheritance
You see inheritance being used all across the Flash platform. Anything on the stage is extending DisplayObject, all events ere extending a base Event class. Inheritance creates inheritance chains, where the class at the end of that chain inherits all the way to the base of the chain.
Canvas -> Container -> UIComponent -> FlexSprite -> Sprite -> DisplayObjectContainer -> InteractiveObject -> DisplayObject -> EventDispatcher -> Object
This is the inheritance chain of the Canvas class in Flex, for example.
Implementing the Interface
The second approach for utilizing polymorphism in Actionscript 3 is through interfaces. Not to be confused with graphical user interfaces, interfaces are a programming language construct used to create classes that implement a specific contract. In Actionscript 3, a class can implement multiple interfaces.
//IAnimal.as
package
{
public interface IAnimal
{
function getName():String;
function eat():void;
function talk():void;
}
}
The first thing we can see is that IAnimal declares several functions. These functions are the same as the ones declared in the abstract Animal class above. Missing from these functions is the public namespace, as well as the brackets constituting the function body. Interfaces allow only public function declarations. You cannot declare properties, static, or private methods in an interface. Why not? An interface represents a contract that lets outside objects know what public methods can be accessed on a class that implements the interface. Since outside objects can't access private methods, they don't belong on the interface. Public properties can't be added to an interface to enforce good encapsulation. A class should manage its own state internally. For this reason, you can declare getters and setters for properties in an interface allowing access to the properties via the class API. Following this approach, the class will still maintain its own internal state:
//this is perfectly valid in an interface function get myProperty():String; function set myProperty(v:String):void;
The following classes implement the IAnimal interface. Notice that they don't extend any class. In addition, with interfaces there is no need to use the override syntax. There is no super class or methods to override.
//Mouse.as
package
{
public class Mouse implements IAnimal
{
public function Mouse()
{
}
public function getName():String
{
return "Mouse";
}
public function eat():void
{
trace("nom nom nom cheese");
}
public function talk():void
{
trace("squeek squeek");
}
}
}
//Pig.as
package
{
public class Pig implements IAnimal
{
public function Pig()
{
}
public function getName():String
{
return "Pig";
}
public function eat():void
{
trace("nom nom nom poo");
}
public function talk():void
{
trace("oink oink");
}
}
}
//Polymorphism.as
package
{
import flash.display.Sprite;
public class Polymorphism extends Sprite
{
protected var menagerie:Array = [];
public function Polymorphism()
{
//create some animals that implement IAnimal
var pig:IAnimal = new Pig();
var mouse:IAnimal = new Mouse();
menagerie = [ pig, mouse ];
//make the IAnimal instances perform
for each(var animal:IAnimal in menagerie)
{
//animal does its thing
trace(animal.getName() + " implements IAnimal")
animal.eat();
animal.talk();
trace("----");
}
}
}
}
As you can see from this second example, the results are very similar. Each IAnimal in the loop shares the common interface, allowing us access to the methods of that interface. The actual class implementations don't matter.
Pig implements IAnimal nom nom nom poo oink oink ---- Mouse implements IAnimal nom nom nom cheese squeek squeek ----
Interfaces and class inheritance are by no means mutually exclusive concepts. A class can extend a single super class, and one or many interfaces:
//Horse.as
package
{
public class Horse extends Animal implements IAnimal
{
public function Horse()
{
super("Horse");
}
override public function getName():String
{
return name;
}
override public function eat():void
{
trace("nom nom nom hay");
}
override public function talk():void
{
trace("nayyyy nayyyy");
}
}
}
Horse is a class that extends Animal and implements IAnimal.
Interfaces In Practice
This is obviously a less than useful example, unless you are making a farm or zoo simulator. I routinely use these concepts in my applications for:
Services - For example, I recently created IImageService. This interface was implemented by FlickrImageService, FacebookImageService, and an XMLImageService class. All of these services shared the common interface that allowed me to create a configurable application that didn't care where images came from. It was expecting an IImageService, but the implementation and actual guts of the methods required by the interface wasn't a concern to the broader application.
DataTypes - You see this a lot in the Flex framework. ICollectionView is implemented by ArrayCollection and several other collection classes. This allows you to use a class that implements ICollectionView as a data provider for many of the Flex components.
Event Responders - IResponder is one of the interfaces in the Flex framework that I make frequent use of. Classes that implement this interface can be dropped in as responders for asynchronous remote methods.
Containers - IContainer is a Flex framework interface that defines a class that functions as a container for visual classes.
By approaching your code with interfaces in mind, you make your classes much more reusable. Future developers are able to substitute new implementations that follow the interface contract and greatly opens up the possibilities for extension. When we code against interfaces, life is much easier when it come time to unit test our applications. Services, data classes, and other implementations can be easily substituted with mock objects in the tests, allowing us to test our classes in isolation, without regard to their integration with the broader application.
Further Reading
These concepts are covered in some depth in Actionscript 3 Design Patterns. It is an all around excellent book.
Essential Actionscript 3 also does a really good job of explaining inheritance, interfaces, and polymorphism.
Head First Design Patterns uses Java for its examples, but it is extremely well written and the concepts translate universally to other OOP languages