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:
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 {}
    }
}

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:
private var _disablerEffect:DisablerEffect;

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:
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;
}

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:
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;
}

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:
public function set disablerEffect(effect:DisablerEffect):void {
    this._disablerEffect = effect;
}

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:
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;
        }
    }
}

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:
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;
        }
    }
}

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:
import com.mysite.utils.ButtonDisabler;
var buttonArray:Array = [b1, b2, b3, b4, b5];
var bd:ButtonDisabler = new ButtonDisabler(buttonArray);

First, we need to change the import line here so that we import all the classes from the utils package:
import com.mysite.utils.*;

Next, add this line underneath all the other code:
bd.disablerEffect = new ScaleEffect();

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:
bd.disablerEffect = new AlphaEffect();

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:
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);
    }
}

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