Multiple Inheritance in ActionScript 3

| 11 Comments | No TrackBacks

Did you know ActionScript 3 supports multiple inheritance? Here's how...

Multiple inheritance is actually possible with AS3, despite popular belief that it's not supported. It's not really multiple inheritance in the true sense of the word going by the strict definition... but there's a way to get the job done in AS3 that behaves almost like the real thing.

What I'm about to show you is not something I would advocate as best practice. Nor is it something that should be (ab)used simply because its available. I'm not going to get into the debate about Composition vs. Inheritance and how Multiple Inheritance fits into the picture. If you're here, it's probably because you want to know how to use this technique. I assume you also know then the various repercussions (and if not, at least have the ability to google them). As they say, "with great power comes great responsibility".

Various disclaimers aside, here we go...

When you to simulate use multiple inheritance, there are three things you need to do:

  1. You need to create an interface that defines the methods you want to use.
  2. You need to create a default implementation for that interface.
  3. You need to merge the default implementation for the interface into the class you want the methods available in, and mark the class as implementing the interface.

It's the last step, the merging of the default implementation, that simulates multiple inheritance. "But Darron," you say, "an interface can't have a default implementation!". To which I say, "You're absolutely correct!"... now what?

...drum roll...

Did you forget that ActionScript 3 is a dynamic language and supports a long-since-forgotten-about-since-ActionScript-1-days but still-supported-because-its-in-the-spec #include pragma?

This is where it gets ugly, but stay with me. We'll create an interface, and then write the implementation for that interface in a free-standing .as file that contains just loose ActionScript code. Then, like magic, we'll include that file in our class and have the compiler automatically insert the code. The result? A class that implements an interface, with a default implementation that's "separate" from the class, and "re-usable" through many classes.

Here's a concrete example to further drive home the idea:

// In CartoonCharacter.as
interface CartoonCharacter
{
   function setSpeechBubbleText( text:String ):void;
}
// In ChartoonCharacter_impl.as
public function setSpeechBubbleText( text:String )
{
   trace( "setting speech bubble text to: " + text );
}
// In CartoonDog.as
public class CartoonDog extends Sprite implements CartoonCharacter
{
  // Check out that bling bling.. fo shizzle!
  #include "CartoonCharacter_impl.as";

  public function CartoonDog()
  {
    // Constructor
  }
}
// In CartoonCat.as
public class CartoonCat extends Sprite implements CartoonCharacter
{
  // Here it is again... oh no he didn't!
  #include "CartoonCharacter_impl.as";

  public function CartoonCat()
  {
    // Constructor
  }
}

From the above series of code blocks, hopefully you can see what happened. In our "impl" file we just have a method name and a method body. The method body is the default implementation for the CartoonCharacter interface. In our CartoonDog and CartoonCat classes, we include that default implementation and implement the interface.

If you ask the dog if he's a CartoonCharacter, he'll surely respond that he is. Likewise with the cat...

if ( dog is CartoonCharacter )
{
  trace( "bow to-tha wow, yo!" );
}

.. but by having the implementation in a separate ActionScript file, it allows us to "pretend" that both dog and cat inherit from CartoonCharacter. When we change the "impl" file, we change the behavior for all of the classes that #include the file. This is important because, in this use case, we have to extend a display object class so we can be added on screen.

Now, I know the first question will be "Why not just make CartoonCharacter a class that extends Sprite and have Dog and Cat extend that?", to which I say, "you completely missed the point of this article."

All that aside, there are a few gotchas to using this technique. In no particular order:

  • You can't override the default implementation. Since we're #include-ing, using the override keyword and trying to roll your own won't cut it.
  • If you include many "impl" files and they have naming collisions (methods with the same names), you'll get a compiler error and the class won't compile. This is a good thing in my book.
  • If your default implementation uses other classes, you have to be careful of the "import" statements since the class that's #include-ing the file needs to import those classes.
  • No real help fom FlexBuilder, but that's to be expected.
  • .. add your own "you suck, this technique is stupid and doesn't work because of XXX" reason here.

In the import case, I've been keeping a separate "MyInterface_imports.as" file, and then #include-ing it at the top of the class with the other imports. There can still be some issues here with multiple inclusion of the same class, but that's not a compiler error (at least, not yet anyway).

Anyway, like I said, use with caution. There are definitely cases where this approach works well, and there are other cases where it's better to change your architecture and avoid it. I leave it as a thought exercise to you to know when to use it and when not to. Don't go crazy kids, you might want to keep the training wheels on for this one...

No TrackBacks

TrackBack URL: http://www.darronschall.com/mt/mt-tb.cgi/127

11 Comments

Your CartoonCat example class uses CartoonDog as the class name, but CartoonCat as the constructor name. Might want to change that before someone gets confused!

Darron,

The summary of your blog should deliver stronger message. It can go like this:
"Kids, I just showed you a hole that adults from Adobe overlooked. Please try to forget everything I just said. Do not do it at home. Keep your program design simple. Promise?" :)

Peace

Well put, it's like debating religon...

Don't go there. :)

Which I forgot about.

Peace, Mike

@Josh - Thanks, fixed.

@Yakov - It wasn't overlooked at all. In fact, this technique is used in the Flex 2 component framework. Peter Hall touched on it here as well: http://www.peterjoel.com/blog/index.php?archive=2004_04_01_archive.xml#108094882595285837

It's not "evil" to use this technique (like Mike says, this is somewhat of a religious debate), but it definitely has the capability of being abused.

Better to know about this technique, understand the pros and cons of it compared to other approaches, and think for yourself, than to blindly do something that everyone else does "just because that's how you do it."

Point taken though. ;-)

Darron,

Interesting technique, but if I ever have to touch code using it, I will personally smack down whoever wrote it (and probably give you dirty looks next time I see you).

IMHO, it is pretty evil looking, and I would hate to inherit a code base that used it. It's surprising to me that the Flex team would use it.

Cheers,
Grant.

>personally smack down whoever wrote it

ROFL - Being somewhat of a purist I can definitely understand that feeling. I have considered this technique for AOP'sh type of stuff but have never gone there.

Regardless, it's always fun to exploit the quirks of the language / player. :)

As I said, it's a religious debate. Anytime someone mentions multiple inheritance, controversy ensues. :-)

The bottom line is, as "bad" as Multiple Inheritance is proclaimed to be, there are situations where it makes more sense conceptually and actually ends up with a cleaner overall system archtecutre (implementation quirks aside). If you follow coding standards when using this technique in AS3, it's not as evil as your gut reaction might lead you to believe.

But then again, I've done quite a bit of C++, so I might be biased here.

For a "it's not all bad" viewpoint, check out this article on the subject: http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=167&rl=1

Doesn't it actually duplicate the compiled bytecode of the included code for every class? (it is like a cut and past)
If abused it may produce very large classes.

Well....that's.....evil&ugly. I was already amazed to see the Flex 2 Framwork used includes; but this is just pure evil. But hey, there's lots of other things that are not player native, but are possible through evil exploits; like....sound!

@Madarco - Yes, the code will get compiled into multiple classes. I wanted to list this as one of the downsides, but I guess I just missed it. I'm not entirely sure how much of an issue it really is though, since compression works best on repeated data. If you over use this technique, it will probably come back to bite you though.

@Ralph - Exactly. The implementation might not be pretty, but when used correctly the end result can be beautiful. :-)

"this is just pure evil..." is a statement that makes me immediately stop listening. It means "I don't understand it and so I don't like it" (sorry).

Objectively, it's a great solution for two similar classes that would otherwise implement a large set of members that are exactly the same. For example, layout code for components that add properties and methods for smartly laying out positioning. Lets say I have components that are MovieClip based, and some that are TextField based. So I have UIComponent and UITextField that extend MovieClip and TextField respectively, but they implement a similar interface and have a lot of duplicate code. Whenever I make changes/fixes in one I have to remember to update the other or I'll be in trouble.

Is it clear why an #include could help in this case? It actually makes changes easier to manage and prevents potential errors by minimizing duplicate code.

This is just an example where you don't have the option to change your inheritance.

If anyone happens to inherit a project from me that uses these techniques I assure you it will be documented and make perfect sense for the situation (and most likely be very rare).

I also agree that if you don't understand it then don't use it, because you will not accidentally use it correctly.

Leave a comment



About this Entry

This page contains a single entry by darron published on October 10, 2006 12:09 PM.

A Flex 2 Closeable Tab Navigator Component was the previous entry in this blog.

The Cookbook is here... is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Archives

OpenID accepted here Learn more about OpenID
Powered by Movable Type 5.02