ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Another Useful, Reusable AS3 Class, "ButtonDisabler"
http://www.actionscript.org/resources/articles/856/1/Another-Useful-Reusable-AS3-Class-ButtonDisabler/Page1.html
Jody Hall
My interest in Flash started mostly because of a Jib-Jab cartoon ("This Land") in 2004. I'm the author of a feature I call "Mazoons," which are a combination of mazes and cartoons. In 2002, I even had a book published, "Super Silly Mazes." I'm not a professional programmer, but making my mazes interactive by programming them with Flash became a hobby/obsession of mine, to the point where I have now learned more than I bargained for. Lately I'm working on a new website about Flash and Actionscript 3.0 called The Flash Connection
By Jody Hall
Published on December 19, 2008
 
In this tutorial, we will build a class together that will automate the task of giving any group of buttons some "button disabling" behavior. You will learn about how to make the class handle any type of button. You will learn about how to decide what tasks ought to be "built-in" to the class, and which tasks are best managed by outside code. You will also learn about interfaces and polymorphism (painlessly, I promise). What you will learn about AS3 classes and OOP is the important thing, but at the same time, I think the ButtonDisabler class is something you wlll find handy and useful in the future. And I hope you'll also realize that if you can make something like this, there are any number of other similar things that you can make as well. It just takes some imagination.

Setting up the fla file with the buttons
You may be familiar with this scenario: You have a group of buttons. You want to have them behave so that if you click one of them, it becomes the active button, which won't allow you to click on it again until after you've selected another. Each time you click a button, it becomes the currently selected button, and the previously selected button becomes clickable again. This has many applications--for example, it could be used for a website: You might have four or five buttons representing your different web pages, like "Home," "About Us," "Products," "Links," and "Contact." If you're currently on the "About Us" page, you want to disallow another click on the "About Us" button, because you're already there. You may have even coded this before. And if you've coded it more than once, well, that just might be a clue that maybe this would be a good candidate for a reusable class.

I actually just recently thought up the idea for this class myself, because I thought it would be a handy class to have. Then when I wrote it, I was really happy with it, and only after the fact did I start thinking it might make a cool subject for a tutorial. So, it still being fresh in my mind, I'm going to take you from the initial concept to the finished product in short, easy steps. Here we go! Ready?

So the goal here is to take a set of buttons, send them to the class, and the class will give them the button disabling behavior described above. First of all I should make clear that many times when I talk about buttons, I usually mean MovieClips being used as buttons, and not  necessarily the Button symbol (or the SimpleButton class). Many long-time users of Flash have realized that MovieClips can do anything Buttons can do, and so much more. Anyway, in the succeeding discussion, when I say "button" (lowercase b), it can sometimes mean MovieClip or Sprite being used as a Button. But if I say "Button" (uppercase B), it will always refer to the Button symbol.

However, the class that we're going to write here really should be able to handle any group of buttons, whether they're Buttons, MovieClips, Sprites, or even a mixture of those. I think when you write classes, you go through a kind of mental exercise where you try to abstract things and make your class as versatile as it can be. It's almost as though you're programming for other programmers! You picture in your mind how you or others are going to be using your class, and you want to make it as easy to use as possible. Not only that, but you want to make it generic enough so that nobody will ever have to go in and modify it.

Start Flash, and open a new Flash File (Actionscript 3.0). Draw Something on the stage. A small circle or an oval will do. Select it, and press F8 to convert it to a symbol. Give it a library name of "Btn," and choose MovieClip as the type. Place the registration point in the center. Click OK. Give the instance on stage an instance name of "b1." Drag out four more instances from the library and place them to the right of the first one. Give these other buttons instance names of b2 through b5 (and remember what I just said about "buttons"--I'm actually talking about MovieClip instances here, of course).

Now you should have a row of buttons, with instance names of b1 through b5. We're keeping it simple for this example; the buttons don't have any text on them, for example. But really, the buttons you use can be as elaborate as you want. Our class's code isn't going to care what the buttons look like, or how they're positioned on the stage, or how many of them there are, or anything like that, it's only going to concern itself with giving them a certain behavior. This will help make it all the more reusable.

The code that creates an instance of the class we'll write could be placed in a document class. But I want to stay focused on the ButtonDisabler class and not confuse anyone or get side-tracked. So we'll just place the code in the fla file's timeline, on frame 1 of an actions layer. So, add a new layer to the timeline and name it "actions." Click on the first frame, and press F9 to get the actions panel. The first order of business is to list the buttons in an array, so type in this code:
[as]var buttonArray:Array = [b1, b2, b3, b4, b5];[/as]
You can save this fla file as "button_disabler_test.fla" anywhere on your hard drive. We'll leave this file for now, but you should leave it open in Flash so that you can come back to it. I know we didn't write any code other than to list the buttons in an array, but at least that gets us thinking about the buttons as a group, and it's this array that will eventually get sent to our class, which we will write next. On to the good stuff!

(Here's a sneak peek at the button behavior I described, just to give you an idea of what we're making here:)




Writing the ButtonDisabler class
Create a new Actionscript file. Type in the following, which is just the structure of a basic class (the stuff you always have to type in when you write a class!). Note the line that contains the package statement. We're going to save this to a package. If you want, substitute the word "mysite" for the actual name of your website, or whatever name you'd like to use as a place to store your classes. If you're confused about any of this, check out my first tutorial, Make your own reusable classes using Flash and AS3 and at least read the first two pages, where I explain packages and the classpath.
[as]package com.mysite.utils {
   
    public class ButtonDisabler {
       
        private var buttons:Array = new Array();
       
        public function ButtonDisabler (theButtons:Array) {
            buttons = theButtons;
        }
    }
}[/as]
Save the file as "ButtonDisabler.as" in the com.mysite.utils folder. If the utils folder doesn't exist, create it. If none of the folders exist, you didn't read my first tutorial! Click the link above, learn about packages and the classpath, and then come back.

All the class does so far is accepts an array and stores it as a private class member (or property). When our client code (the code in the fla file) creates a new instance of this class, it must pass an array as an argument in the parentheses. Specifying an array as a parameter here, and not giving it a default value, makes it a required parameter. But that's okay, as this class won't be of any use unless it's sent an array of buttons to work on. That's the whole purpose of the class, so there's no disadvantage here to having a required parameter. So, we might as well send it what it needs up front.

It's important to realize that once the constructor runs, the class will then have a reference to the buttons that are on the stage in the fla file. They're the exact same set of buttons! The class stores the array locally as "buttons," and in the fla file, it's known as "buttonArray," but both names refer to one and the same array object. In fact, the same name could have been used for both, since they're each in their own different scope.

Next, we're going to write code that loops through the array and adds an event listener to each of the buttons. Add this line to the constructor, right after the buttons = theButtons line:
[as]addListeners();[/as]
Next, we'll write the addListeners() function:
[as]private function addListeners():void {
    for(var i:int = 0; i < buttons.length; i++) {
        buttons[i].addEventListener(MouseEvent.CLICK, clickHandler);
    }
}[/as]
This function runs through a for loop, which loops through all of the buttons and adds the same event listener to each one. The event to listen for is the MouseEvent.CLICK event. At this point, a little alarm bell should go off in your head: We need to import the MouseEvent class if we're going to use mouse events, so add this line right after the package statement, where the import statements go:
[as]import flash.events.MouseEvent;[/as]
Next, we need to write the clickHandler function. At this point, the thought process goes something like this: Inside the clickHandler function, we need to disable whichever button is currently being clicked, that is, prevent it from being clicked again. Secondly, we need to restore the click functionality to whatever button might have been clicked on a previous time. So we need variables to keep track of these things. We'll call the button being clicked "currentButton" and the previously clicked button "lastClicked." Add these variables to the variables list:
[as]private var currentButton:MovieClip;
private var lastClicked:MovieClip;[/as]
Now, of course, you'll need to add MovieClip to your list of import statements:
[as]import flash.display.MovieClip;[/as]
I mentioned before that we want this class to work with any kind of Button, Sprite, or MovieClip, but for now, we'll type these variables as MovieClip. This will be okay for our intial testing purposes, as the buttons in the fla are MovieClip instances, but later we'll abstract it some more to include the other types. That way, I can save that explanation for later on, too. Here's the clickHandler function:
[as]private function clickHandler(event:MouseEvent):void {
    if(lastClicked != null) {
        lastClicked.mouseEnabled = true;
    }
    var clickedButton:MovieClip = MovieClip(event.currentTarget);
    currentButton = buttons[buttons.indexOf(clickedButton)];
    currentButton.mouseEnabled = false;
    lastClicked = currentButton;
}[/as]
The first thing this code does is checks whether lastClicked does not have a null value. Because if the value of lastClicked is null, then the click that we are getting right now is the first click on any button. But if the value of lastClicked is not null, then there must have been a previous click, and the click that we are now getting is either the second click or later. So if there was a previously clicked button, it was also formerly disabled, so we want to restore it to being clickable again.

The next thing the code does is detects which button received the click, and this is stored in a temporary local variable called "clickedButton." The next line sets the value of currentButton equal to whichever button in the buttons array is the one that was clicked. It does this by using the array method "indexOf()" to search through the array and determine the index of the button that was clicked. This is kind of a shortcut to looping through the array yourself and comparing each button to "clickedButton." Next, currentButton has its mouseEnabled property set to false, which disables it from being clicked on again. Finally, the value of lastClicked is set equal to the currentButton. This will be the button that will be reinstated the next time one of the group of buttons is clicked on.

Alternatively, you could use the removeEventListener() method to disable the buttons, and then use addEventListener to restore them to being clickable again. That works just as well, and I doubt that there are any performance issues for something this simple, or any reasons for preferring one way over another. To me, it just seems easier to just set mouseEnabled to true or false. It's a lot less typing!

There are a couple of further things needed to be done in this class before we can test it. There needs to be some kind of visual feedback to let us know that a button is disabled or enabled. One visual cue would be the hand cursor that you get when buttonMode is set to true. So go back and add this line to the addListeners() function, inside the loop where the buttons are being assigned the listener:
[as]buttons[i].buttonMode = true;[/as]
All of the buttons will now have a hand cursor. But when a button is disabled, it won't, it will have the arrow cursor instead. As a further visual cue, though, let's set the alpha of the disabled button to 50%, and when a button is restored, restore its alpha back to 100% again. Here's the modified clickHandler function with this alpha setting added in:
[as]private function clickHandler(event:MouseEvent):void {
    if(lastClicked != null) {
        lastClicked.mouseEnabled = true;
        lastClicked.alpha = 1;
    }
    var clickedButton:MovieClip = MovieClip(event.currentTarget);
    currentButton = buttons[buttons.indexOf(clickedButton)];
    currentButton.mouseEnabled = false;
    currentButton.alpha = 0.5;
    lastClicked = currentButton;
}[/as]
Putting it all together, here is the class listing so far:
[as]package com.mysite.utils {
   
    import flash.events.MouseEvent;
    import flash.display.MovieClip;
   
    public class ButtonDisabler {
       
        private var buttons:Array = new Array();
        private var currentButton:MovieClip;
        private var lastClicked:MovieClip;
       
        public function ButtonDisabler (theButtons:Array) {
            buttons = theButtons;
            addListeners();
        }
        private function addListeners():void {
            for(var i:int = 0; i < buttons.length; i++) {
                buttons[i].addEventListener(MouseEvent.CLICK, clickHandler);
                buttons[i].buttonMode = true;
            }
        }
        private function clickHandler(event:MouseEvent):void {
            if(lastClicked != null) {
                lastClicked.mouseEnabled = true;
                lastClicked.alpha = 1;
            }
            var clickedButton:MovieClip = MovieClip(event.currentTarget);
            currentButton = buttons[buttons.indexOf(clickedButton)];
            currentButton.mouseEnabled = false;
            currentButton.alpha = 0.5;
            lastClicked = currentButton;
        }
    }
}[/as]
Go ahead and save this class if you haven't already.

Next, let's turn our attention back to the fla file, and test this out. First, the fla file code needs to import the class we just made:
[as]import com.mysite.utils.ButtonDisabler;[/as]
Next, we need to create an instance of the ButtonDisabler class and pass it our array of buttons:
[as]var bd:ButtonDisabler = new ButtonDisabler(buttonArray);[/as]
So the complete code listing on frame 1 of the fla file is this:
[as]import com.mysite.utils.ButtonDisabler;
var buttonArray:Array = [b1, b2, b3, b4, b5];
var bd:ButtonDisabler = new ButtonDisabler(buttonArray);[/as]
Press CTRL-ENTER to test the movie. When you click on one of the buttons, it will fade to 50% alpha, and the hand cursor will go away. If you then click on another button, the one formerly clicked on will be restored to full alpha and become clickable again, and the newly clicked button will be disabled.

At this point, we can congratulate ourselves on having made a well-behaved class that we can easily re-use to give any group of MovieClip buttons some button-disabling behavior. However, there are a couple of problems: One of them is that our class is hard-wired to always use MovieClips, and we want the class to be more versatile than that. Later, you might have a project where you want to use this for a group of Button symbols, or even Sprites, or even some combination of these other types. Our class should be able to handle it all!

The other issue is that our class is hard-wired to always make the buttons fade to 50% alpha and then be restored to 100% alpha. That limits you to only one effect! What if, in another project, the effect you want is different. For example, you might want a button to scale to one and a half times its size when it's disabled, then go back to normal size when it's restored. Or some other similar effect. Or maybe, no effect at all.

Not only that, but if we distribute this class for others to use, they may want to use it in these other ways, too. But, ideally, nobody should be required to go in and edit this class again, it's just not good OOP! Clearly, we have a bit more work to do on this class! And we'll take on these other issues on the next page!

Further abstracting the ButtonDisabler class
The first thing to address with the ButtonDisabler class is the issue of making it versatile enough to handle different kinds of buttons: Buttons, MovieClips, or Sprites. To do this, we just need to think about what all those symbols have in common. They are all descended from more basic classes. So let's go back and choose a common ancestor that is low enough to encompass them all, but high enough to give us the functionality we need. So what we should do is study the inheritance chain for these classes, and to do that we just consult the help files, and look up each class. In the help system, find and expand "ActionScript 3.0 Language and Components Reference," then expand "All Classes," then find the listing for MovieClip. It will have an inheritance chain like this:

MovieClip -> Sprite -> DisplayObjectContainer -> InteractiveObject -> DisplayObject -> EventDispatcher -> Object

and the inheritance chain for SimpleButton looks like this:

SimpleButton -> InteractiveObject -> DisplayObject -> EventDispatcher -> Object

Notice that MovieClip is descended from Sprite, so MovieClip is just a more specialized kind of Sprite. So we don't need to also look up Sprite. You can see that both are descended from DisplayObjectContainer, so either one can serve as a container for other display objects. The next basic type going up the chain is InteractiveObject, and it is here that they first share common ancestry with SimpleButton, as SimpleButton is immediately descended from InteractiveObject. Therefore, we've found the first common denominator! In our class, we will no longer give the buttons a type of MovieClip, or even SimpleButton or Sprite. If we always type them as being of the "InteractiveObject" type instead, then that will encompass all of them, and we can treat them all equally. By the way, this is polymorphism! The idea that "...an instance of a subclass can be used anywhere an instance of its superclass is expected." --Colin Moock, "Essential Actionscript 3.0", page 115.

Now, in the variable list, the variables for currentButton and lastClicked can be changed to this more generic type instead:
[as]private var currentButton:InteractiveObject;
private var lastClicked:InteractiveObject;[/as]
Inside the clickHandler function, the local variable clickedButton can have its type changed too:
[as]var clickedButton:InteractiveObject = InteractiveObject(event.currentTarget);[/as]

By making these changes, we are making our class way more useful. Someone can now send the class an array of buttons that are of any of the three types, or even a mixture! We do need to allow for at least one difference, though: SimpleButtons differ from MovieClips and Sprites, in that, whereas you need to set buttonMode to true to get a hand cursor for MovieClips and Sprites, a SimpleButton instance will automatically already have a hand cursor. In fact, the SimpleButton class doesn't even have a buttonMode property. It's already a button, so a buttonMode property wouldn't make any sense--it's not going to be used any other way than as a button. So to allow for that difference, in the addListeners() method, as we loop through the buttons, we need to test whether a button in the array is a MovieClip or a Sprite, and only set buttonMode to true if that's the case:
[as]if(buttons[i] is MovieClip || buttons[i] is Sprite) {
    buttons[i].buttonMode = true;
}[/as]
This code works just fine; you just need to make sure that both the MovieClip and Sprite classes are being imported. However, notice that the inheritance chain revealed that MovieClip and Sprite are both DisplayObjectContainers. Therefore, if something is a DisplayObjectContainer, it has to be either a MovieClip or a Sprite! That means that the above code could also be written like this:
[as]if(buttons[i] is DisplayObjectContainer) {
    buttons[i].buttonMode = true;
}[/as]
And now the only class that must be imported is DisplayObjectContainer. So now the import statement block can be modified to look like this:
[as]import flash.events.MouseEvent;
import flash.display.InteractiveObject;
import flash.display.DisplayObjectContainer;[/as]
Here's the complete class listing as it stands so far:
[as]package com.mysite.utils {
   
    import flash.events.MouseEvent;
    import flash.display.InteractiveObject
    import flash.display.DisplayObjectContainer;
   
    public class ButtonDisabler {
       
        private var buttons:Array = new Array();
        private var currentButton:InteractiveObject;
        private var lastClicked:InteractiveObject;
       
        public function ButtonDisabler (theButtons:Array) {
            buttons = theButtons;
            addListeners();
        }
        private function addListeners():void {
            for(var i:int = 0; i < buttons.length; i++) {
                buttons[i].addEventListener(MouseEvent.CLICK, clickHandler);
                if(buttons[i] is DisplayObjectContainer) {
                    buttons[i].buttonMode = true;
                }
            }
        }
        private function clickHandler(event:MouseEvent):void {
            if(lastClicked != null) {
                lastClicked.mouseEnabled = true;
                lastClicked.alpha = 1;
            }
            var clickedButton:InteractiveObject = InteractiveObject(event.currentTarget);
            currentButton = buttons[buttons.indexOf(clickedButton)];
            currentButton.mouseEnabled = false;
            currentButton.alpha = 0.5;
            lastClicked = currentButton;
        }
    }
}[/as]
Save this new version. The class looks a bit different than it's former incarnation, and if you test the fla file at this point, you'll find that it runs and behaves identically. However, now it will also handle a group of buttons that are actual SimpleButtons or Sprites, not just MovieClips. As an exercise, you can prove it to yourself by right-clicking the "Btn" symbol in the library. Choose "properties," then select "Button" as the type. Click on each button instance on the stage, and in the properties inspector, pull down the dropdown that says "MovieClip" and change it to "Button." Now all of the instances on the stage are Buttons rather than MovieClips. Now you can test the movie, and you'll see it behaves identically.

On the next page, we'll take on that next issue: moving the alpha-changing effect to the outside of the class.


Separating the things that vary
I'm going to digress from the ButtonDisabler class for a few paragraphs, and talk about interfaces and types. Then, when I get back to the ButtonDisabler class, you'll see how this topic applies.

When I was first learning about classes and objects, it seemed like a lot to take in all at once. Maybe you feel that way, too. So whenever I would read about interfaces, I would just kind of push aside the information, figuring it was enough to just understand classes for the time being. I never really read a good definition of interfaces, so I didn't fully understand what they were, and hadn't even the foggiest notion of what I would use them for, so I always just figured I'd get to that later on. Now, having read a few books on design patterns, and finally having a much better understanding of the subject, I still wish I would have gotten a simpler explanation right from the very start. Because, like a lot of things, it's not as complex as the fancy (and scary) words make it sound.

It's important to differentiate the programming concept called "interfaces" from whatever other definition of "interface" the word makes you think of. Simply put, we're not talking about a graphical user interface with buttons and other GUI elements. So don't think about that kind of interface, but what we will talk about is not completely dissimilar, so I'll draw an analogy from that. You interact with a GUI by clicking it's buttons, pulling down its dropdown boxes, and checking off its checkboxes, etc, etc. Meanwhile, behind the scenes, the actual programming that makes these things work is shielded from you. If you want to interact with the program, you have to do it through the GUI interface.

Similarly, a class's interface consists of the public methods that are written into it. Period. A class is often described as being a "black box," meaning that it's a completely closed system, except for the interface. And that's as it should be. All the programming that makes the class work is shielded from you, all you need to know is the names of the public methods, what kind of arguments they expect, and what kind of data they return (if any). Pretend for just a moment that you are some big block of client code. You've just created an instance of some class. Now, if you want to interact with that instance, you have to do it through its public methods, because that's its interface!

So, it's important to realize that you're already using interfaces when you're making classes. Every class, if it has public methods, has an interface. When you make a class, you're making a template from which to build objects, or instances of the class. Each object that you build from the class is also considered to be of that class's type. The type is the same as the name of the class. You've seen this if you've done my other tutorials. In one, I made a Quiz application and created a class called QuizQuestion. When you create objects from this class, they can be considered to be of the QuizQuestion type. You've seen it in this tutorial, too; the SimpleButtons, Sprites, and MovieClips are considered to also be those types. But, as we saw, they could also all be considered to be of one of their supertypes as well, InteractiveObject. This is because at some point in their inheritance chain, they share a common interface. This makes them interchangeable (able to stand in for one another), and that's polymorphism.

Now back to the ButtonDisabler class, because I think you'll get this better if you see a simple example of it at work. What we need is a way to make different effects for changing the appearance of the buttons, but make it external to the class. What we'll do is create a new set of classes just for this purpose. Inside the ButtonDisabler class, we want to build in some kind of button changing behavior, but we don't want to build in a specific behavior like we did before, we want to leave it open to change. What if, similar to the example of the different kinds of buttons that could stand in for each other, we create a set of classes that can stand in for each other because they share a common type? There will be a base class that defines a couple of methods, but we'll leave it to subclasses to give these methods any specific behavior. We'll make two public methods in this base class, "morph" and "unMorph." There's nothing special about these names, I just chose them because I think they nicely describe what they will do: morph will apply some sort of change to the button being disabled, and unMorph will undo this change.

Open a new Actionscript File. Type (or paste) the following in the code window:
[as]package com.mysite.utils {
   
    import flash.display.InteractiveObject;
    //abstract class
    public class DisablerEffect {
        //abstract methods
        public function morph(clip:InteractiveObject):void {}
        public function unMorph(clip:InteractiveObject):void {}
    }
}[/as]
Save this file to the com.mysite.utils folder and name it DisablerEffect.as.

The morph and unMorph methods obviously need to be sent a reference to whatever button they're going to be working on, so it's necessary to give them a parameter. For this parameter, it made sense to just keep InteractiveObject as the type. This made it necessary to import the InteractiveObject class. In Actionscript 3.0, there is no construct in place to designate an abstract class, like there is in Java. You can still make abstract classes, but it's up to the programmer to remember that a particular class is to be considered an abstract class. If a class is an abstract class, this means that you never want to make instances of it directly. It exists just for the purpose of giving its subclasses a common type (and interface!), and perhaps defining some default behavior (although we're not doing that in this case). For example, the InteractiveObject class is never instantiated directly, but you saw how it served to give a common type to the subclasses: SimpleButton, Sprite, and MovieClip. So the comments in the above class are there to remind us (and others) that this class and its methods are intended to be abstract.

Now let's work on the ButtonDisabler class again. We'll give it an instance variable, and it will be of our new "DisablerEffect" type. Add this line to the variables list of ButtonDisabler:
[as]private var _disablerEffect:DisablerEffect;[/as]
Note that we don't have to import the DisablerEffect class, because it's in the same package as ButtonDisabler.

Now we'll do a little work on the clickHandler function. Here it is as we last saw it:
[as]private function clickHandler(event:MouseEvent):void {
    if(lastClicked != null) {
        lastClicked.mouseEnabled = true;
        lastClicked.alpha = 1;
    }
    var clickedButton:InteractiveObject = InteractiveObject(event.currentTarget);
    currentButton = buttons[buttons.indexOf(clickedButton)];
    currentButton.mouseEnabled = false;
    currentButton.alpha = 0.5;
    lastClicked = currentButton;
}[/as]
We need to take out those lines that set the alpha, because that's the specific behavior we want to take out. We'll replace them with calls to the methods in the DisablerEffect class. We'll call these methods using our instance variable _disablerEffect. Here's the new version:
[as]private function clickHandler(event:MouseEvent):void {
    if(lastClicked != null) {
        lastClicked.mouseEnabled = true;
        _disablerEffect.unMorph(lastClicked);
    }
    var clickedButton:InteractiveObject = InteractiveObject(event.currentTarget);
    currentButton = buttons[buttons.indexOf(clickedButton)];
    currentButton.mouseEnabled = false;
    _disablerEffect.morph(currentButton);
    lastClicked = currentButton;
}[/as]
You may be saying at this point, yeah, but the DisablerEffect class just has empty methods in it that don't do anything. And, besides that, the _disablerEffect instance variable hasn't even been given a value yet, so it's still null. Well, we're about to get to that. Let's write a setter function that will allow us to set a value for the _disablerEffect variable. Add this function to the class:
[as]public function set disablerEffect(effect:DisablerEffect):void {
    this._disablerEffect = effect;
}[/as]
This function will allow us to give the _disablerEffect variable a value, and do it from outside the class. If you are unclear about how to use getters and setters, you need to review my previous tutorials. In short, though, the get and set keywords allow us to write methods, but from outside the class, use them as though they were properties. You'll see this in action shortly.

Next. let's write a subclass so that we can get some actual effects working for the ButtonDisabler class. Open a new Actionscript file. Type (or paste) the following in the code window:
[as]package com.mysite.utils {
    import flash.display.InteractiveObject;
   
    public class ScaleEffect extends DisablerEffect {
        override public function morph(clip:InteractiveObject):void {
            clip.scaleX = clip.scaleY = 1.5;
        }
        override public function unMorph(clip:InteractiveObject):void {
            clip.scaleX = clip.scaleY = 1;
        }
    }
}[/as]
Save this file to com.mysite.utils and name it ScaleEffect.as. This class extends (subclasses) DisablerEffect. So it inherits the public methods morph and unMorph, but it overrides them. When you override a method, you give it a new version. In this case, each method takes whatever button is passed to it and modifies its scale. Now, let's write another effects class. Open another new Actionscript file and type or paste in the following:
[as]package com.mysite.utils {
    import flash.display.InteractiveObject;
   
    public class AlphaEffect extends DisablerEffect {
        override public function morph(clip:InteractiveObject):void {
            clip.alpha = 0.5;
        }
        override public function unMorph(clip:InteractiveObject):void {
            clip.alpha = 1;
        }
    }
}[/as]
Save this one to com.mysite.utils and name it AlphaEffect.as. This class's effect is the same as the one that was formerly built in. Next, let's turn our attention back to the fla file again, and all this will come together and make sense. The code on frame 1 of the fla file, as we last left it, reads:
[as]import com.mysite.utils.ButtonDisabler;
var buttonArray:Array = [b1, b2, b3, b4, b5];
var bd:ButtonDisabler = new ButtonDisabler(buttonArray);[/as]
First, we need to change the import line here so that we import all the classes from the utils package:
[as]import com.mysite.utils.*;[/as]
Next, add this line underneath all the other code:
[as]bd.disablerEffect = new ScaleEffect();[/as]
Press CTRL-ENTER to test the movie. The disabled button will scale one and a half times as big, and, when re-enabled, scales back to normal size again. Now, here's the magic of polymorphism. Change that last line to this:
[as]bd.disablerEffect = new AlphaEffect();[/as]
Test the movie again. Now, the effect has changed. The disabled button is faded to half alpha, and, when re-enabled, goes back to normal alpha again. Pretty cool, huh? Now you can completely change the effect just by changing this one line of code!

So let's review how all this works. The ButtonDisabler class has an instance variable of the DisablerEffect type. It simply calls the methods of this variable at the right times, passing it a reference to the button to change. The subclasses we wrote, ScaleEffect and AlphaEffect, both subclassed the DisablerEffect class. Both of them overrode the DisablerEffect class's public methods (and filled in the details of what those methods ought to do). Consequently, these two classes share a common interface. So they can both be treated as being of this common type, and one can be substituted for the other. The ButtonDisabler class calls the morph and unMorph methods on its _disablerEffect instance, but is has no knowledge of what actions are being carried out by them.

Now you can write any number of effects classes for the ButtonDisabler class to use. And you can do so without ever having to modify the ButtonDisabler class itself. All you have to do is subclass the DisablerEffect class, override the morph and unMorph public methods (just pattern it after the examples above), and then write your client code to use an instance of your new effects class when you set the _disablerEffect variable (just like we did in the above). Because the _disablerEffect variable in the ButtonDisabler class was "typed" as being of type DisablerEffect (and the setter method is also expecting a variable of this type) and not some more specific type, we're able to pass in subclasses of DisablerEffect interchangeably. This is known as programming to an interface rather than an implementation, and is one of the key principles of reusable software design.

I said we need never modify the ButtonDisabler class again. But there is one more change we ought to make to it. The possibility exists that someone will make an instance of ButtonDisabler, and neglect to give it a disablerEffect. This may be on accident, or even on purpose, as perhaps someone might want to use this class but not give it an effect at all. So we need to allow for this in our code. Change the clickHandler function in the ButtonDisabler class to this:
[as]private function clickHandler(event:MouseEvent):void {
    if(lastClicked != null) {
        lastClicked.mouseEnabled = true;
        if(_disablerEffect != null) {
            _disablerEffect.unMorph(lastClicked);
        }
    }
    var clickedButton:InteractiveObject = InteractiveObject(event.currentTarget);
    currentButton = buttons[buttons.indexOf(clickedButton)];
    currentButton.mouseEnabled = false;
    currentButton.alpha = 0.5;
    if(_disablerEffect != null) {
        _disablerEffect.morph(currentButton);
    }
}[/as]
It ought to be pretty clear to you what this does. If the client code has not used the setter function set a value for the _disablerEffect variable, it will have a value of null. So the above code only calls _disablerEffect's methods if the variable has been given a value by the outside code. Now our class ought to be bullet proof. It will still work whether a button disabling effect is set or not.

On a final note, I should add in here that anything specific that you want the buttons to do when they're clicked should be handled by the client code (in this case, the fla file). For example, an "About Us" button like I described earlier. The code that makes it load the "About Us" page should not be built into the ButtonDisabler class. And there is no problem with adding another listener to the buttons, and having another click handler function in the fla file. I just thought I'd add that as a final footnote, as I just realized that you'll of course want to make the buttons in an actual application do more than just disable and re-enable each other.

As always, I hope you enjoyed this tutorial, and that you were perhaps inspired by it to apply the concepts to your own creations. I hope you actually did this tutorial, and built it as we went along. I'm a believer in learning by doing. But I realize there are people who would rather just read the article and download the files. So I'm attaching a ZIP for your convenience.

Finally, I appreciate all the nice comments I received on my previous tutorials. Thank you so much!

Jody Hall