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

Encapsulation

Encapsulation is what makes a code object an object. If you have a tail, four legs, a cold nose and a bark, you do not have a dog. You just have a collection of parts that make up a dog. When you bring all of the doggy parts together, you know that each part is a part but collectively, you do not think of parts but a reality sui generis. That is, a dog is an object unto itself and not doggy parts that happen to hang together. Encapsulation has a similar effect on a collection of operations and properties.

Encapsulation has been used synonymously with other terms such as component and module. In the context of OOP, encapsulation is often called a black box, meaning you can see it do certain things but you cannot see the inner workings. Actually, a lot of things we deal with all the time are black boxes, such as our dog. We can see the dog do a lot of different things, and we can interact with the dog. However, we really don't know (or usually care) about how the physiology of the dog works—dogs are not transparent. They're black boxes.

The good thing about the concept of a black box is that we don't have to worry about the inner workings or parts. We just have to know how we can deal with it, secure in the knowledge that whatever makes the black box work is fine as long as it works as we think it should.

Hiding Your Data from Bad Consequences

To see why you might want to encapsulate your data, we'll take a look at two programs. One is not encapsulated, leading to unwanted consequences, and the other is encapsulated, preventing strange results.

If you make a dog object, you may want to include an operation that includes the way a dog communicates. For purposes of illustration, we'll include a method called dogTalk that will let the dog make different sounds. The dog's communication will include the following:

  • Woof

  • Whine

  • Howl

  • Grrrr

We'll start off with bad OOP to illustrate how you may end up with something you don't want in your dog's vocabulary. Example 1-5 is not encapsulated and will potentially embarrass your dog object:

Example 1-5. NoEncap.as

package
{
    //This is BAD OOP -- No encapsulation
    import flash.text.TextField;
    import flash.display.Sprite;

    public class NoEncap extends Sprite
    {
        public var dogTalk:String="Woof, woof!";
        public var textFld:TextField=new TextField();

        public function NoEncap()
        {
            addChild(textFld);
            textFld.x=100;
            textFld.y=100;
        }
        function showDogTalk()
        {
            textFld.text=dogTalk;
        }
    }
}

As a black box, you should not be able to change the internal workings of a class, but this class is wide open, as you will see. You can interact with an encapsulated object through its interface, but you should not allow an implementation to make any changes it wants. Example 1-6 breaks into the object and changes it in ways you don't want:

Example 1-6. TestNoEncap.as

package
{
    import flash.display.Sprite;

    public class TestNoEncap extends Sprite
    {
        public var noEncap:NoEncap;
        public function TestNoEncap()
        {
            noEncap=new NoEncap();
            noEncap.dogTalk="Meow";
            noEncap.showDogTalk();
            addChild(noEncap);
        }
    }
}

Open a new Flash document file, and, in the Document class window, type in TestNoEncap. When you test the file, you'll see "Meow" appear on the screen. Such a response from your dog object is all wrong. Dogs don't meow and cats don't bark. However, that's what can happen when you don't encapsulate your class. When you multiply that by every un-encapsulated class you use, you can imagine the mess you might have. So let's find a fix for this.

Private variables

The easiest way to insure encapsulation is to use private variables. The private statement in ActionScript 3.0, whether it's used with variables, constants or methods (functions) makes sure that only the class that defines or declares it can use it. This not only shuts out implementations that attempt to assign any value they want, but it also excludes subclasses. (This is a difference from ActionScript 2.0; so watch out for it if you're converting an application from ActionScript 2.0 to ActionScript 3.0.)

To see how the private statement will change how the application works, Example 1-7 changes the NoEncap class by adding the private statement to the variables:

Example 1-7. Encap.as

package
{
    //This is GOOD OOP -- It has encapsulation
    import flash.text.TextField;
    import flash.display.Sprite;

    public class Encap extends Sprite
    {
        private var dogTalk:String="Woof, woof!";
        private var textFld:TextField=new TextField();

        public function Encap()
        {
            addChild(textFld);
            textFld.x=100;
            textFld.y=100;
        }
        function showDogTalk()
        {
            textFld.text=dogTalk;
        }
    }
}

Also, minor changes have to be made to the test file. The supertype implemented must be changed. Example 1-8 shows the new test class, TestEncap , for the Encap class.

Example 1-8. TestEncap.as

package
{
    import flash.display.Sprite;

    public class TestEncap extends Sprite
    {
        public var encap:Encap
        public function TestEncap()
        {
            encap=new Encap();
            encap.dogTalk="Meow";
            encap.showDogTalk();
            addChild(encap);
        }
    }
}

Go ahead and test it by changing the Document class name to TestEncap. This time, though, you'll get the following error in the Complier Errors panel:

Line 11: 1178: Attempted access of inaccessible property dogTalk through
 a reference with static type Encap.
Source: encap.dogTalk="Meow";

It shows the source of the error to be the line:

encap.dogTalk="Meow";

That error reflects the fact that it attempted to access a private variable outside the class. To fix the script, comment out the offending line:

//encap.dogTalk="Meow";

Try testing it again. This second time, everything works fine, except, you don't get the dog object expressing "Meow." You see "Woof, woof."

You may be thinking that private variables really limit what you can do. Suppose you want the dog object to howl, growl or whimper? How do you make the changes dynamically? Preventing an encapsulated object from doing something wrong is one thing, but how can an object be set up to accept variables?

The many meanings of interface

In this book, you will find the term interface used in different contexts, and each context gives the term a slightly different meaning. (Thanks a lot!) Up to this point, you're probably familiar with terms like UI (user interface) or GUI (graphic user interface). These terms refer to different tools you use to interact with a program. For example, a button is a common UI in Flash. When you click a button, something predictable happens. You may not know how it happens (or care), but you know that if you press the button, the video will play, a different page will appear, or an animation will start. So if you understand the basic concept of a UI, you should be able to understand how an interface works with an object.

With a UI, the black box is the application you're using, whether it's shopping at eBay or using a word processor. If you follow certain rules and use the different UIs in the appropriate manner, you get what you want. In the same way, an encapsulated object is a black box, and the interface describes the ways you can interact with it programmatically. It's the UI for the object.

Design Patterns: Elements of Reusable Object-Oriented Software (page 13) nicely clarifies object interfaces and their signatures. An object's signature is its operation name, parameters and return datatype. Figure 1-5 graphically shows the makeup of a typical object's signature.

Object's signature

Figure 1-5. Object's signature

All of an object's signatures defined by its operations is the interface. In this context then, the interface for the object constitutes the rules for access, list of services, and controls for it.

Note

Wait! There's more! Later in this chapter you will find an interface statement as part of ActionScript 3.0's lexicon, which is a whole different use of the term, interface. You will also find the term used synonymously with supertype elsewhere in this book. All of the different uses of interface will be explained in time.

Before getting bogged down in contextual definitions, it's time to move on to see what an encapsulated object's interface looks like. The following section does just that.

Getters and setters

The most common way to enforce encapsulation but to give implementations access to an object is with getter and setter interfaces. The object controls both access to it and what is allowed. Keeping our example of a dog object, we know that the dog has a limited vocabulary, and it's not one that includes "Meow." So, we'll just make a setter that allows only the dog's limited vocabulary.

A setter method includes parameter variables of some kind, and an algorithm that allows certain things and excludes others. However, most setters just assign the parameter value to a private member regardless of its value. The algorithm and everything else in the function that makes up the method is invisible to the implementation, and if the wrong parameter or wrong datatype is entered, either an error message appears or the value will not be passed. The following shows the general structure of a setter:

function setterMethod(parameter)
{
    if(parameter=="OK")
    {
        private variable = parameter;
    }
}

So the trick is to have the implementation pass any data to the object as a parameter, and if the parameter is acceptable, the private variable is assigned the parameter's value. This is a very general overview of the setter, but it shows the essentials of how it works.

Compared to setters, getter methods are pretty straightforward. In Example 1-7, the showDogTalk() method is the getter function. The getter method's job is to provide data for the implementation. Thus, while the original example doesn't have a setter method, it does have a getter. The setter makes sure that the client gets only what it's supposed to get.

In Example 1-9, the private variable, dogTalk, is not assigned a default value. However, the variable is still used in both the setter and getter methods. As you will see when you test the new class, EncapSet, the implementation has access to the private variable through the setter's interface.

Example 1-9. EncapSet.as

package
{
    //This is BETTER OOP -- It's got encapsulation
    //plus a decent interface for an object

    import flash.text.TextField;
    import flash.display.Sprite;

    public class EncapSet extends Sprite
    {
        private var dogTalk:String;
        private var textFld:TextField=new TextField();

        public function EncapSet()
        {
            addChild(textFld);
            textFld.x=100;
            textFld.y=100;
        }

        //Setter
        function setDogTalk(bowWow:String)
        {
            switch (bowWow)
            {
                case "Woof" :
                    dogTalk=bowWow;
                    break;

                case "Whine" :
                    dogTalk=bowWow;
                    break;

                case "Grrrr" :
                    dogTalk=bowWow;
                    break;

                case "Howl" :
                    dogTalk=bowWow;
                    break;

                default :
                    dogTalk="Not dog talk!";
            }
        }

        //Rendering value
        function showDogTalk()
        {
            textFld.text=dogTalk;
        }
    }
}

As you can see in Example 1-9, the setter method allows only the four expressions we had listed for a dog. Anything else (including "Meow") is not allowed. Next, Example 1-10 shows how to interface with the encapsulated dog object:

Example 1-10. TestEncapSet.as

package
{
    import flash.display.Sprite;

    public class TestEncapSet extends Sprite
    {
        private var encapSet:EncapSet
        public function TestEncapSet()
        {
            encapSet=new EncapSet();
            encapSet.setDogTalk("Howl");
            encapSet.showDogTalk();
            addChild(encapSet);
        }
    }
}

Enter TestEncapSet in the Document class of the FLA file and test it. As you will see, the string "Howl" is perfectly acceptable. Now, test it again using "Meow." This time, you will see that the object rejected the kitty cat sound as "Not dog talk." This arrangement represents the best of both worlds; encapsulation and a way to execute operations within an encapsulated object.

The get and set methods

Another way to maintain encapsulation and hide the information in your objects is to use the ActionScript 3.0 get and set methods. Some programmers find it awkward to create their own getter and setter methods as we did in the previous section, preferring the simplicity of the get accessor and set mutator.

Note

Accessors and mutators versus Frankenstein: they sound like something from a horror flick, but the terms accessor and mutator are used to describe getters and setters. The accessor (getter) does access or get information, and that's perfectly reasonable. But mutators? Well, if we think about it, a mutation does refer to a change, and when we set information, we do indeed change it. It too makes perfect sense. So if you're happily reading an article on design patterns and you see the terms accessor or mutator, don't let it creep you out.

In looking at how get and set are used, a key feature is the absence of parentheses. Changes (setting) are not accomplished by adding values. Rather, the getters and setters are treated like properties where values are assigned or retrieved through assignment.

Example 1-11 and Example 1-12 show how you can lock up your encapsulated objects using getters and setters with the get and set methods.

Example 1-11. FlowerShop.as

package
{
    public class FlowerShop
    {
        private var buds:String;

        public function FlowerShop():void {}

        //Getter function
        public function get flowers():String
        {
            return buds;
        }

        //Setter function
        public function set flowers(floral:String):void
        {
            buds=floral;
        }
    }
}

In Example 1-12, keep in mind that flowers is a method, and not a property. However, setting and getting values using the flowers() method looks exactly like setting and getting a property value.

Example 1-12. Send Flowers.as

package
{
    import flash.display.Sprite;

    public class SendFlowers extends Sprite
    {
        public function SendFlowers()
        {
            var trueLove:FlowerShop = new FlowerShop();
            //Set values
            trueLove.flowers="A dozen roses";
            //Get values
            trace(trueLove.flowers);
            //Set different values
            trueLove.flowers="And a dozen more....";
            //Get the changed values
            trace(trueLove.flowers);
        }
    }
}

Using Encapsulation and Design Patterns

This section on encapsulation has been fairly long. The reason for the attention to encapsulation is because of its importance to good OOP; it's a crucial element in design patterns. Of the 24 original design patterns, only 4 have a class scope and the remaining 20 have an object scope. Rather than relying on inheritance (which is discussed in the next section), the great majority of design patterns rely on composition.

Later in this chapter, in the section, "Favor Composition," you will see how design patterns are made up of several different objects. For the design patterns to work the way they are intended, object encapsulation is essential.

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