Interface Programming, using ‘implements’ instead of ‘overrides’
Suppose we are a Beastmaster, and have created a class MonsterFarm whose list will contain several species of fantastic creatures. We want to create a Centaur and Human class, and populate the farm with one instance of each:
Human:
package { public class Human { public function Human() { //human here } } }
Centaur:
package { public class Centaur { public function Centaur() { //Centaur here } } }
MonsterFarm:
package { public class MonsterFarm { var monsters:Array = []; public function MonsterFarm() { trace('monster farm is open!'); var centaur:Centaur = new Centaur(); var human:Human = new Human(); monsters.push(centaur); monsters.push(human); } } }
We want to be able to call upon all the creatures within the farm to speak, so both the Human and Centaur will need a 'speak' method. Additionally, the farm will need to treat all creatures similarly, without having to know their Type.
The most straightforward solution would be to extend both the Human and Centaur classes from a common class like Creature, and then move the speak function into that class.
The problem now is that Centaurs and humans speak differently, so we'd probably create another speak() method in both the Centaur and human classes that override the method in Creature.
This is all well and good, but what happens if Centaur didn't extend from Creature, but instead extended from Monster (which in turn extends from EnemyCreature)? Since classes can only extend from a single class, we would need to work out an elaborate encapsulation and overriding method chain for all the speak-able creatures. Worse still, when we call all our creatures to speak() in the farm, we may no longer be able to call all Creatures anymore, but instead call their common parent which can only safely be assumed as Object.
Enter interfaces!
Unlike classes, interfaces define their methods and not their implementation, which means that it is up to each class to implement the methods themselves. This means that a class can implement multiple interfaces.
For our Centaur and Human, we create a new interface named ICharacterActions which will contain a speak() method:
package { public interface ICharacterActions { function speak(); } }
Both the Centaur and Human classes can now 'implement' this interface, and promise to supply its methods similarly to an override (without the 'override' syntax). These must also be public methods!
Human:
package { public class Human implements ICharacterActions { public function Human() { } public function speak() { trace('Good Morrow!'); } } }
Centaur (note that this is extending from a class unrelated to Human!):
package { public class Centaur extends Monster implements ICharacterActions { public function Centaur() { } public function speak() { trace('neigh!'); } } }
Although the Centaur and Human classes have different parents, the MonsterFarm can still call them all to speak() by simply to their common interface: ICharacterActions
//in MonsterFarmfor each(var monster:ICharacterActions in monsters) { monster.speak(); } //traces: //neigh! //Good Morrow!
With interfaces, we have opened up a myriad of possibilities for the MonsterFarm - we can populate it with any kind of creature, with only the speakable ones implementing the respective interface. We can also add additional interfaces and refine them further like 'ISpeak', 'IFight' so that speaking creatures need not also be fighting creatures, and vice-versa. The performance is also much quicker than overrides.
The catch?
Implementing an interface is like setting a contract - a creature that implements the ICharacterActions interface must contain all the methods defined in the interface or it will cause an error:
1044: Interface method * in namespace * not
implemented by class *.
Methods in interfaces must not have any curly braces '{}' that signify their implementation, so no 'default code' can be executed here (which doesn't matter, because you need to implement the method to avoid the 1044 error above anyway).
For a working example of today's files, download the 'Interface Programming Example' file from the files page.