O'Reilly logo

ActionScript 3.0 Design Patterns by Chandima Cumaranatunge, William Sanders

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Polymorphism

The root of polymorphism is a word that could easily replace it to describe the process— metamorphosis. The term is from the Greek metamorphoun, meaning to transform. One definition describes metamorphosis as a magic-like transformation—something a sorcerer would do. If you like, think of polymorphism as giving the programmer the power of a sorcerer.

Generating Polymorphism Using An Abstract Class

Another definition of polymorphism is that it allows for many (poly) forms (morph). In Example 1-21 through Example 1-25, you saw how the abstractMethod had more than a single form. That's polymorphism. To see it in a more practical application, consider people's taste in music and emerging forms of music. Suppose you create a class with a method set up to show a person's musical tastes, and then build your application using that method for the different genres. Example 1-26 through Example 1-31 provide a simple example of such an application. The root abstract class is named Polymorphism in honor of the concept it illustrates.

Example 1-26. Polymorphism.as

package
{
    //Abstract class
    public class Polymorphism
    {
        public function myMusic():void
        {
            //Reserve details for subclasses
        }
    }
}

Example 1-27. Rock.as

package
{
    public class Rock extends Polymorphism
    {
        override public function myMusic():void
        {
            trace("Play Jimmie");
        }
    }
}

Example 1-28. Classic.as

package
{
    public class Classic extends Polymorphism
    {
        override public function myMusic():void
        {
            trace("Play Mozart");
        }
    }
}

Example 1-29. Country.as

package
{
    public class Country extends Polymorphism
    {
        override public function myMusic():void
        {
            trace("Play Willie");
        }
    }
}

Example 1-30. Jazz.as

package
{
    public class Jazz extends Polymorphism
    {
        override public function myMusic():void
        {
            trace("Play Coltrane");
        }
    }
}

Example 1-31. PlayMusic.as

package
{
    import flash.display.Sprite;
    public class PlayMusic extends Sprite
    {
        var rock:Polymorphism;
        var classic:Polymorphism;
        var country:Polymorphism;
        var jazz:Polymorphism;

        public function PlayMusic():void
        {
            rock=new Rock();
            rock.myMusic();
            classic=new Classic();
            classic.myMusic();
            country=new Country();
            country.myMusic();
            jazz=new Jazz();
            jazz.myMusic();
        }
    }
}

When you test the program, you'll see the following:

Play Jimmie
Play Mozart
Play Willie
Play Coltrane

As you can see, it's not rocket science and hardly cloaked in mystery. It's just polymorphism. All instances were typed to the abstract class Polymorphism. Then they were instantiated using the subclasses, and each launched the same method, myMusic(). However, with each usage, even though all shared the same datatype (or supertype), Polymorphism, each instance's use of the method generates a unique outcome.

Looking at the program and your MP3 library, you may be thinking, "That's not nearly enough music categories." What about R&B, Country, Alternative, Hip-Hop, and Cowboy music? (Cowboy?) We couldn't agree more. Go ahead and create new subclasses using polymorphism to add all the categories you want. (This is not one of those exercises where the answer is at the end of the chapter or the back of the book. You're on your own. Programmers don't get polymorphism right without writing their own code. See if you've got the right stuff.) By the way, notice that you can make all the changes you want with polymorphism without having to change any of the other classes. That's the whole point of polymorphism.

Implementing Polymorphism with Interfaces

The ActionScript 3.0 interface statement and structure is the other powerful tool for polymorphism in both object-oriented programming and design patterns. Suppose you're building an e-business site. You have no idea how many new products the site will have, but you know the site will have many different products that change regularly. You're going to need something that will handle a wide variety of products, and a way of displaying them.

You know that you'll need the following operations, but you're not sure about the details:

  • Description

  • Price

  • Product display

One way to set up an e-business site would be to create a class that has methods for all three operations. However, suppose you find out that the client wants different kinds of displays for the different products. She wants dynamically loaded video for video products (e.g. TV sets), sound for sound products (e.g. MP3 players) and graphic images for all other products. The operations for both description and price methods are pretty standard, but the display method is going to have to be very flexible. That's where polymorphism comes in. We could use an abstract class, but to provide ever more flexibility just in case other unanticipated requirements crop up, we'll use an interface.

Example 1-32 through Example 1-36 make up the e-business application. The interface, IBiz, contains the primary operations with unique signatures but not any details. This will allow us to create the unique details we need as long as we keep all the signatures the same.

Example 1-32. IBiz.as

package
{
    public interface IBiz
    {
        function productDescribe():String;
        function productPrice(price:Number):String;
        function productDisplay(product:String):void;
    }
}

Example 1-33 introduces something new. The Plasma class extends one class, Sprite, and implements another, IBiz. Placing a video on the stage requires a Sprite object, but we still need the IBiz interface methods; so using both the extends and implements statements, we're able to have the best of both worlds.

Example 1-33. Plasma.as

package
{
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.media.Video;
    import flash.display.Sprite;

    public class Plasma extends Sprite implements IBiz
    {
        private var ns:NetStream;
        private var vid:Video;
        private var priceNow:Number;


        public function productDescribe():String
        {
            return "42 inch TV with Plasma screen";
        }

        public function productPrice(price:Number):String
        {
            priceNow=price;
            return "$" + priceNow + "\n";
        }

        public function productDisplay(flv:String):void
        {
            var nc:NetConnection=new NetConnection();
            nc.connect(null);
            ns=new NetStream(nc);
            ns.play(flv);
            vid=new Video();
            vid.attachNetStream(ns);
            addChild(vid);
        }
    }
}

In comparing Example 1-33 with Example 1-34, both the productDescribe() and productPrice() methods look pretty much the same other than the literal used in the return statement in the productDescribe(). In fact, by adding a string parameter to the productDescribe() method, we could make them identical. However, the point of this application is to demonstrate polymorphism, and so creating different forms of the methods is intentional.

You can see the practicality of polymorphism by comparing the productDisplay() methods in the two examples. Example 1-33 is set up to play a video and Example 1-34 a sound. However, note that the identical signatures are maintained in both examples.

Example 1-34. MP3Player.as

package
{
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.net.URLRequest;

    public class MP3Player implements IBiz
    {
        private var sound:Sound;
        private var playChannel:SoundChannel;
        private var doPlay:URLRequest;
        private var MP3Price:Number;

        public function productDescribe():String
        {
            return "MP3Player with 1 Terabyte of Memory";
        }

        public function productPrice(price:Number):String
        {
            MP3Price=price;
            return "$" + MP3Price + "\n";
        }

        public function productDisplay(song:String):void
        {
            sound=new Sound();
            playChannel=new SoundChannel();
            doPlay=new URLRequest(song);
            sound.load(doPlay);
            playChannel=sound.play();
        }
    }
}

Example 1-35 has further differences in its productDisplay() details. Instead of either video or sound operations, it contains operations for loading files. Yet, like the other two, the signature and interfaces are still the same.

Example 1-35. Computers.as

package
{
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.display.Sprite;

    public class Computers extends Sprite implements IBiz
    {
        private var hotz:Number;
        private var loadPix:Loader;
        private var namePix:URLRequest;

        public function productDescribe():String
        {
            return "New 10 gHz processor, 30 gigabyte RAM, includes
                32 inch screen";
        }

        public function productPrice(price:Number):String
        {
            hotz=price;
            return "$" + hotz + "\n";
        }

        public function productDisplay(computer:String):void
        {
            loadPix=new Loader();
            namePix=new URLRequest(computer);
            loadPix.load(namePix);
            addChild(loadPix);
        }
    }
}

Now that all the classes and the interface are complete, all that's left is a class to test the application. As you've seen above, good OOP and preparation for working with design patterns suggests programming to the interface and not the implementation as we've been doing. However, in looking at Example 1-36, all of the typing (setting the datatype) is to the Plasma, MP3Player, and Computers implementations and not the IBiz interface. This is because each of the implementations extended the Sprite class. So we're dealing with two supertypes, IBiz and Sprite. To test the application, the instances are typed as one of the specific implementations instead of the interface. In the section "Program to Interfaces over Implementations" later in this chapter, you'll see how to construct your interface and implementation so that you can still type to the interface when there's more than a single type in the implementation. (In Example 1-20 you can see how an interface structure is programmed to the interface instead of an implementation.)

Example 1-36. DoBusiness.as

package
{
    import flash.display.Sprite;

    public class DoBusiness extends Sprite
    {
        public function DoBusiness()
        {
            //TV
            var tv:Plasma=new Plasma();
            trace(tv.productDescribe());
            trace(tv.productPrice(855));
            tv.productDisplay("plasma.flv");
            tv.x=160;
            tv.y=110;
            addChild(tv);

            //MP3
            var mp3:MP3Player=new MP3Player();
            trace(mp3.productDescribe());
            trace(mp3.productPrice(245));
            mp3.productDisplay("bongo.mp3");

            //Computers
            var computers:Computers=new Computers();
            trace(computers.productDescribe());
            trace(computers.productPrice(1200));
            computers.productDisplay("whizbang.gif");
            addChild(computers);
        }
    }
}

You will need an FLV file, an MP3 file and a GIF file for this application. Name the FLV file plasma.flv, the MP3 file bongo.mp3, and the GIF file whizbang.gif. The contents are unimportant but the names and file types are important.

Open a new Flash document, save it in the same folder with the rest of the application, and type in DoBusiness in the Document class window. When you test it, the Output window shows the following:

42 inch TV with Plasma screen
$855

MP3Player with 1 Terabyte of Memory
$245

New 10 gHz processor, 30 gigabyte RAM, includes 32 inch flat screen monitor
$1200

In addition to messages in the Output window, you should see something like the following on the stage:

Graphic file and video appear on the stage

Figure 1-6. Graphic file and video appear on the stage

In addition to the visible graphics, you should also hear the sound played by the MP3 file. (This multimedia experience has been brought to you by polymorphism and the letter P.)

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required