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

Program to Interfaces over Implementations

To understand why the authors of design patterns encourage programming to interfaces over implementations, you need to first understand the general goal of flexibility and reusability. Second, you need to appreciate the problem of managing dependency in large programs.

As we have noted in this chapter, the overall goal of design patterns is to create reusable code. In order to meet this overall goal, the code must be flexible. This does not mean that your application runs better or compiles faster. All it does is help you create code that you can reuse in other projects. The more time and effort you spend, the larger the team engaged in working with the code, the more important this overall goal,

Managing Dependency

The need for software flexibility leads to the need to manage dependency. When your code depends on a specific implementation, and the implementation changes, your client dependency leads to unexpected results or fails to run altogether. By depending on interfaces, your code is decoupled from the implementation, allowing variation in the implementation. This does not mean you get rid of dependency, but instead you just manage it more flexibly. A key element of this approach is to separate the design from the implementation. By doing so, you separate the client from the implementation as well.

As we have seen, you can use the ActionScript 3.0 interface structure or an abstract class to set up this kind of flexible dependence. However, when you use either, you must be aware of the way in which to manage the dependency. If you use an interface structure, any change in the interface will cause failure in all of the clients that use the interface. For example, suppose you have five methods in an interface. You decide that you need two more. As soon as you add the two new methods to your interface, your client is broken. So, if you use the interface structure, you must treat the interface as set in stone. If you want to add new functionality, simply create a new interface.

The alternative to using the interface structure is to use abstract classes. While the abstract class structure is not supported in ActionScript 3.0 as interfaces are, you can easily create a class to do everything an abstract class does. As we saw in several examples in this chapter beginning with Example 1-3, creating and using an abstract class is simply adhering to the rules that abstract classes follow anyway. For example, abstract classes are never directly implemented.

The advantage of an abstract class over an interface is that you won't destroy a client when you add methods to the base class. All of the abstract function must be overridden to be used in a unique manner, but if your client has no use for a new method, by doing nothing, the method is inherited but not employed or changed. On the other hand, every single method in an interface structure must be implemented.

A further advantage of an abstract class is that you can add default behaviors and even set up concrete methods inherited by all subclasses. Of course the downside of default behaviors and concrete methods is that a subclass may not want or need the default or concrete methods, and a client may end up doing something unwanted and unexpected if any concrete changes are introduced. Whatever the case, though, management of dependency is easier with the flexibility offered by interfaces and abstract classes over concrete classes and methods.

So the decision of whether to use an interface or abstract class depends on what you want your design to do. If the ability to add more behaviors easily is most important, then abstract classes are a better choice. Alternatively, if you want independence from the base class, then choose an interface structure. No matter what you do, though, you need to think ahead beyond the first version of your application. If your application is built with an eye to future versions and possible ways that it can expand or change, you can better judge what design pattern would best achieve your goals.

Using Complex Interfaces

As you saw in Example 1-36, the program typed the instances to the implementation and not the interfaces as was done in Example 1-20. This was caused by the key methods being part of classes that implemented an interface and extended a class. Because of the way in which the different display objects need to be employed, this dilemma will be a common one in using Flash and ActionScript 3.0. Fortunately, a solution is at hand. (The solution may be considered a workaround instead of the correct usage of the different structures in ActionScript 3.0. However, with it, you can create methods that require some DisplayObject structure and program to the interface instead of the implementation.)

If the interface includes a method to include a DisplayObject type, it can be an integral part of the interface. Because Sprite is a subclass of the DisplayObject, its inclusion in the interface lets you type to the interface when the class you instantiate is a subclass of Sprite (or some other subclass of the DisplayObject, such as MovieClip.)

To see how this works, the application made up of Example 1-39 and Example 1-40 creates a simple video player. The VidPlayer class builds the structural details of the video playing operations. To do so requires that it subclass the Sprite class. By placing a getter method with a DisplayObject type in the IVid interface, the application sets up a way that the client can program to the interface.

Example 1-39. IVid.as

package
{
    import flash.display.DisplayObject;

    public interface IVid
    {
        function playVid(flv:String):void;
        function get displayObject():DisplayObject;
    }
}

The implementation of the IVid interface includes a key element. The displayObject() function is implemented to the DisplayObject class in building the VidPlayer class.

Example 1-40. VidPlayer.as

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

    public class VidPlayer extends Sprite implements IVid
    {
        private var ns:NetStream;
        private var vid:Video;
        private var nc:NetConnection;

        public function get displayObject():DisplayObject
        {
            return this;
        }

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

Keep in mind that a getter method, using the get keyword, looks like a property, and is treated like one. Any reference to displayObject is actually a method request. The trick is to add the instance to the display list, and at the same time call the method that establishes the instance as a DisplayObject. Example 1-41 does just that.

Example 1-41. DoVid.as

package
{
    import flash.display.Sprite;

    public class DoVid extends Sprite
    {
        //Type as Interface
        private var showTime:IVid;

        public function DoVid()
        {
            //Play the video
            showTime=new VidPlayer();
            showTime.playVid("iVid.flv");
            //Include DisplayObject instance
            addChild(showTime.displayObject);
            showTime.displayObject.x=100;
            showTime.displayObject.y=50;
        }
    }
}

In reviewing how the process works, first, the showTime instance is typed to the interface, IVid. Next, showTime instantiates the VidPlayer class that has all the details for playing the video. By doing so, it inherits the Sprite class as well as the IVid interface. Then the showTime client plays the video using the playVid() method. Finally, when the showTime instance is added to the display list with the addChild statement, it is added as both the child of the VidPlayer class and the DisplayObject class by using the displayObject getter. Because the getter, displayObject, is included in the display list, you will not get the following error:

1067: Implicit coercion of a value of type IVid to an unrelated type flash.
display:DisplayObject.

IVid appears in the error because the instance was typed to the interface instead of the implementation. By slipping in the DisplayObject typed getter method, we avoid the error.

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