Tutorial – Understanding Classes in AS3 Part 4

In this tutorial, you’ll learn about the OOP (Object-Oriented Programming) concept of encapsulation, and how to break encapsulation to get stuff done in a decent amount of time.

Technical Correctness vs. Getting Sh*t Done

i’ve been really happy with the way this series has been received. If you’re like me, you’ve had a hard time understanding the big deal about OOP and Classes – you know it’s something you’ve got to bite the bullet and eventually learn, but it seems like a whole lot of extra typing for nothing.

Cat on keyboard

i’ve been typing for friggin’ HOURS!

But for those of us designers-turned-coders who have embraced Classes (or have been forced into using them for a paid project with a deadline), we’ll tell you that when written the “right” way, Classes can free you from writing a lot of code in your next project.

The trouble comes when you know so much about Classes that you write them too correctly. You can definitely spend too much time making a Class all things to all people and for all purposes. “It’s a MegaClass that can be an animated sprite AND a media player! It’s so VERSATILE!” Meanwhile, you either haven’t launched a game in a year, or your clients are calling every other hour wondering how their project is coming along.

Kids in back seat

Are we there yet? Are we there yet? Are we there yet? Are we there yet?

Let’s Recap

In Understanding Classes Part 1, you freed yourself from the timeline. In Part 2, you took a little detour and learned how to piggyback your library to write custom MovieClip symbols using inheritance. In Part 3, you started looking at how code could be organized in separate files to keep your workspace light, and to better organize your work. Now let’s take a look at the next principle of OOP, encaspulation, to see if we can’t turn our Classes into true drag-and-drop Lego bricks.

OOP Encapsulation

Good encapsulation makes dinosaurs happen. Bad encapsulation makes the dinosaurs go crazy and kill people.

Lego, Ikea furniture, and Your Code

Once again, the three principles of Object-Oriented Programming are inheritance, encapsulation, and polymorphism.

Think of your Classes as Lego bricks. You want to build a Media Player brick that works in your current project, that you can lift out and snap into your next project that requires a Media Player, with no extra work required. That’s the dream, anyway.

Lego

You want your code to be less time-consuming. You know – like Lego.

Up to this point, i’ve recommended that you make all of your Classes’ fields (things that a Class is) and methods (things that a Class does) public. Public variables and methods can be accessed by anyone and anything outside the Class.

Let’s look at some code. We have an ImageGallery Class and our Main Class. Main creates an instance (stamp, version, print) of ImageGallery and draws it to the screen using addChild:

package
{
	import flash.display.MovieClip;

	public class ImageGallery extends MovieClip
	{
		public var numberOfImagesICanDisplay:int = 5;
		public function ImageGallery()
		{
			// Display numberOfImagesICanDisplay
		}
	}
}
package
{
	import flash.display.MovieClip;

	public class Main extends MovieClip
	{
		public function Main()
		{
			var imageGallery:ImageGallery = new ImageGallery();
			addChild(imageGallery);
		}
	}
}

So far so good. But take a close look at that variable on ImageGallery:

		public var numberOfImagesICanDisplay:int = 5;

That field is declared public. That means that any idiot (including YOU) can access that field and change it. Let’s be a total jackwad and, from Main, change it to eight million:

		imageGallery.numberOfImagesICanDisplay:int = 8000000;

This messes up ImageGallery’s pyjama party to the very limit. How is ImageGallery supposed to display eight million photos? It’s only really set up to handle five images. There HAS to be a way to protect important variables like numberOfImagesICanDisplay from other ass-hat Classes like Main.

Terminator

i recommend sending the Terminator back in time to prevent yourself from messing with that variable.

Stop Looking at My Privates

Fortunately, there is a better and less future-altering way. If we change public to private, only instances (copies, stamps) of the ImageGallery Class can read and write the value of that field. Hooray!

The way i suggested you write your Classes was completely backards. Public is the most permissive access modifier. Private is the least permissive access modifier.

The rule i’ve read is that you should always start out restrictive, declaring everything private, because once you go public it’s kinda hard to go back to private. (If you were an early bloomer in high school who “went public”, you’ll know how difficult it is to regain your privacy)

Slutty schoolgirl

It’s hard to recover from going public

So declare everything – methods and fields/variables – private. If you find that any outside Classes need to access those methods and fields later, you can open them up by making them public.

What Should I Make Public?

Object-oriented programming eggheads are pretty particular about the kinds of things that should be public, and those that should be private. The example i’ve heard is to think of a device like a vending machine. There are certain client-facing input methods that need to be accessible to the outside world:

  • insert coin
  • push snack button
  • push coin return button

And there are certain things the vending machine is going to return to the person using it:

  • snacks
  • coins
  • error messages (“SOLD OUT”, INCORRECT CHANGE”, “NO CANDY FOR YOU, FATTY”, etc)

So the vending machine accepts input and dispenses output. Everything else is private. The user does not need to be able to control the coils that turn to dispense the snacks. The user has no business figuring out how to make change inside the machine, or managing the vending machine’s inventory of goodies. That’s all internal stuff. That’s private.

Vending Machine

None shall know the labyrinthine secrets of the vending machine! MWA HA HA HA HA!!

Dispatching Events

We already know how to write a public method on a Class, and we learned how to pass a parameter through to the Class. Here’s a snippet from an imaginary vending machine Class:

public function insertCoin(coin:Coin):void
{
	// Tally up the value of the coin that has been inserted
	switch (coin.type)
	{
		case "quarter":
			coinValue += 25;
			break;
		case "dime":
			coinValue += 10;
			break;
		case "nickel":
			coinValue += 5;
			break;
		case "penny":
			// No pennies!
			return coin(coin);
			break;
		default:
			break;
	}
}

And here’s an example of how you might call that function from outside the Class:

vendingMachine.insertCoin(new Quarter());

In Actionscript 3, the proper way to signal to the outside world that something important has happened inside a Class is to fire an even dispatcher. Here’s what that looks like in our imaginary vending machine Class:

// This line goes at the top of the Class where you declare your fields:
public static const RETURN_SNACK:String = "returnSnack";

// And then later, when you declare your methods:
private function returnSnack():void
{
	dispatchEvent(new Event(RETURN_SNACK));
}

And here’s how you listen for that event in the Main Class (or from whichever Class instantiates [makes a copy of] the vending machine):

// Make sure you import the generic Event stuff near the top of the file:
import flash.Events.event;

// This is what your instantiation and listener definition look like:
var vendingMachine:VendingMachine = new VendingMachine();
vendingMachine.addEventListener(VendingMachine.RETURN_SNACK, getASnack);

// And elsewhere, you define the method to respond to the event:
private function getASnack(e:Event)
{
	// HOORAY FOAR SNAKZ!!!
}

Anyone see a problem yet? i do. We’re not really able to pass a parameter back to Main through our dispatchEvent command. So how does Main know what kind of snack it received?

There are a few ways around this. We can store a variable on Main called requestedSnack and take it on faith that vendingMachine returns the correct snack. Or, we can create a public variable on VendingMachine to store the returnedSnack, and in Main’s getASnack method, we refer to vendingMachine.returnedSnack. And then there are super egghead ways to do it that remain mysterious to me.

Nerd

i am not well-versed in the Way of the Nerd.

Let’s Get Ready to Fudge It

But here’s the CHEATERPANTS way of doing things. Using dispatchEvent is nice, because we can plop this vending machine down in any other game or app and it WORKS. It’s encapsulated. There are no dependancies. We just need to listen to all of its dispatched events in the new file, and we’re gold. But to be honest, that’s a whole lot more typing, and i’ve got things to do.

The cheaty way to do it is to pass a reference to Main as a parameter to the VendingMachine Class:

// From Main:
var vendingMachine:VendingMachine = new VendingMachine(this);
// From VendingMachine:
public function VendingMachine(main:MovieClip)
{
}

// And then later in VendingMachine:
private function dispenseSnack(snack:Snack):void
{
	main.getASnack(snack);
}

Hooray! No need to listen for an event, dispatch an event, declare a constant, or tapdance around the return value problem. The vendingMachine instance knows who its daddy is, and can invoke any of Main’s public methods.

Who's Your Daddy

(um … i can’t actually tell, ’cause his head is cut off)

Of course, the drawback is that this breaks encapsulation. You can no longer pick up your VendingMachine Class and move it to a new project, because VendingMachine is dependant on receiving a reference to a Class that has a public method called getASnack() on it.

Here’s how i go about it: if i’m writing something that i think is pretty handy, and it’s abstract enough that i’ll likely use it in future projects, i might do the extra work to write a nicely encaspulated Class. But if it’s sunny outside and all the other kids are playing in the park, and i can hear their joyous squeals through my opened window, i’ll break encapsulation and throw my Class a reference to the Class that instantiated it.

Splash Pad

Encapsulation be damned – the splash pad’s open!

Don’t Cry, Nerds

Dear Eggheads: i haven’t left you out this time. The concept of passing a variable to the constructor function of a Class was explained perfectly clearly here, i hope, but i know how you like to muck things up with big words and impressive terminology. Your term for this technique is “Dependency Injection”, and if you’d like to read a more serious article on it with fewer pictures and bigger words, check out Joel Hooks’s article AS3 Dependency Injection Demystified.

Here’s a solid step towards demystifying it: STOP CALLING IT DEPENDENCY INJECTION. Sheesh almighty.

To read the rest of the Understanding Classes series, or to see more tutorials, check out our Flash and Actionscript 911 feature.

28 thoughts on “Tutorial – Understanding Classes in AS3 Part 4

  1. Merve

    It’s a real shame none of these articles were around a month ago when I had to learn all this stuff.

    I do everything via the cheaterpants method, mainly because I hate as3 events with a passion

    Reply
    1. Ryan

      Merve – there’s absolutely no shame in cheaterpantsing it.

      Please share the series with people who are a month behind you. Let’s save each other the torment!

      Reply
  2. Dorian

    Hate the Event Model, don’t hate the Dispatcha’.

    Custom events can be a pain, but when I finally buckle down and use them I find I see the benefits quickly more often than not.

    Reply
    1. Ryan

      Dorian – What’s your strategy for dealing with the problem of event dispatchers being unable to pass data? Do you just use public fields?

      Reply
  3. Andres F.

    I’ll answer for Dorian,

    You create a custom Event Class for your vending machine that extends from flash.events.Event (say, VendingMachineEvent), set the constant for the event (like your RETURN_SNACK), and add a public variable with the info you want to pass (snack).

    Your return snack method would look like this:

    var event : VendingMachineEvent = new VendingMachineEvent(VendingMachineEvent.RETURN_SNACK);
    event.snack = mySnack;
    dispatchEvent(event);

    Your receiver method getASnack would have (event : VendingMachineEvent ) as the parameter instead. And voila, you can call event.snack and both you and us eggheads are happy :)

    You can re-use this custom event every time you need to send the same variables around. Egghead supremus would say that if the variables you want to send change, you should create a new custom event class. While I advocate that, at times it might get ridiculous (tons and TONS of slightly different events)…and truth be told, we eggheads are pragmatic, too. I’ve sent that convention to hell myself a few times.

    I hope that was clear enough!

    Reply
  4. Pingback: Posts about Actionscript 3 as of September 25, 2009 - Perry Multimedia Blog

  5. axchos@yahoo.com

    Events can be useful when you have lots of “daddies” to report back to. :p

    But yeah, in general I agree. I don’t like to spend extra time on code organization and housekeeping when I could be actually making the game… I’m still working on getting that balance right.

    Also, not sure if you saw this but I included your Understanding Classes in AS3 series as one of three tutorials I recommend for getting started with AS3 game programming. (click my name for the article) Let me know if there are any other tutorials you think I should include. :)

    Reply
  6. Nils N.H.

    Alright! I started reading the tutorials today, and I just went through them like that. The funny pictures actually DO help a lot for motivation or making my subconscious pay attention to the other information.

    Your style of writing resembles that of the Head First book series. The series boasts a philosophy of trying to make the reader “care about the material like it was a tiger charging him.” (Something like that)

    Thanks for helping me get to grips with classes. ^^

    Reply
    1. Ryan

      Nils – i love the Head First series! i have the book on C# and the one on design patterns. i’d say, though, that Kurt Vonnegut influenced me with Breakfast of Champions long before i’d even heard of Head First ;)

      Reply
  7. Bobby

    @Andres F. and Ryan:

    you can shorten your code even shorter by combining the custom event and passing of the variable into one line.

    dispatchEvent(new VendingMachineEvent(VendingMachineEvent.RETURN_SNACK, snack));

    for others wanting to know what the VendingMachineEvent class would look like:


    package
    {
    import flash.events.Event;

    public class VendingMachineEvent extends Event
    {
    public var snack:*;

    public static const RETURN_SNACK:String = "ReturnSnack";

    public function VendingMachineEvent(type:String, snack:*)
    {
    this.snack = snack;
    super(type);
    }
    }
    }

    and then like Andres mentioned, your receiver method:


    private function getASnack(e:VendingMachineEvent):void
    {
    var mySnack:[what type] = e.snack;
    }

    Reply
  8. Freddy

    Those examples are VERY confusing. You can’t just through a few lines of code and expect that it will make sense. Snippet are NOT programs. If you want everybody to understand your ideas please build a REAL example not just lines of codes without any logic or base .private function getASnack(e:Event) becomes private function dispenseSnack(snack:Snack):void that does not make much sense PLEASE:).

    Reply
    1. Ryan Henson Creighton

      Hey, Freddy. These tutorials are about understanding classes, not copying and pasting someone’s working code so that you don’t have to put forth any effort to comprehend the material. There’s plenty of stuff online that you can copy and paste … there are far fewer tutorials that will help you understand what you’re copying and pasting.

      Reply
      1. Freddy

        Again if you don’t build a real example most will say:” Oh yeah I got the idea” But as soon as they will try to build a concrete example base on your vague tutorial they are lost. I am an AS3 Tutor so I know the deal. I bet half forget to link their library Class/Object they want to use and half forget to import their classes (while using several).
        I mean come on what prevent you to show real examples? We are talking about 5 more lines of code at the most.

        Reply
  9. Ben Reynolds

    Haha, I love your writing style! Great info for new and experienced programmers alike. I find myself doing the *exact* same thing you do here with the “cheaterpants” method, and the occasional encapsulation when appropriate.

    Reply
  10. Paul

    Hi Ryan

    Thanks again for these EXCELLENT tutorials. I have learnt SO MUCH from them.

    In the above example that started this post, our two classes are Main and ImageGallery. In Image Gallery you have a variable numberOfImagesICanDisplay:int = 5.

    When the ImageGallery Class is instantiated inside Main the Main Class can trace out the value of numberOfImagesICanDisplay showing that we can pass the value of the variable inside the ImageGallery Class to the Main Class which is instantiating it.

    I tried to change the value of numberOfImagesICanDisplay fromm inside the Main Class by using (as you suggested)

    imageGallery.numberOfImagesICanDisplay:int = 8;

    Allthough I used a more reasonable number.

    However it keeps throwing up the error;

    Main.as, Line 17 1078: Label must be a simple identifier.

    My code is as follows;

    Inside Main.as

    public class Main extends MovieClip
    {
    public function Main()
    {
    var imageGallery:ImageGallery = new ImageGallery();
    addChild(imageGallery);

    }

    function Change ()
    {

    imageGallery.numberOfImagesICanDisplay:int = 8;
    }
    }
    }

    Inside ImageGallery.as

    package
    {
    import flash.display.MovieClip;

    public class ImageGallery extends MovieClip
    {
    public var numberOfImagesICanDisplay:int = 5;
    public function ImageGallery()
    {
    trace(numberOfImagesICanDisplay);
    }
    }
    }

    Any idea what the problem is ?

    Thanks

    Paul

    Reply
    1. Ryan Henson Creighton

      Hey, Paul. Looks like you just have an extra } at the bottom of the Change function. Please also note that in AS3, functions/methods should not begin with a capital letter … that’s a convention they use in C# and certain other languages, but not AS3. It won’t break your code – it’s just a “best practices” thing.

      Reply
  11. Paul

    Hi Ryan

    Any chance of a tutorial focusing only on the principles of Class instantiation and accessing functions within these classes. After 6 months of learning AS3 I can do most things but still struggle to do the above. I have SO MANY AS3 books and have read SO MANY tutorials and they all give single examples that work on their files but when I think I have the principles right and try it with my own classes I get nothing but errors errors errors !!! In frustration I just compile a single SUPER SUPER class with ALL the code from ALL the classes. It works but its not good practice. NONE of the books or tutorials seem to think its worth spending any time on so they just glance over one example and leave you to it. Comparing examples from different sites they dont all seem to be the same.

    Maybe I am just stupid !

    Thanks !

    Paul

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>