![]() |
|
||||
|
|
Chapter two in all its sucking progress... (!) Introduction In this chapter we will move, slowly, from the theoretical world to the real world. The concepts of OO, although quite simple in the end, do take some time to digest. What you might have found though, is that once you understand classes, inheritance, and objects, you still feel a bit lost when sitting down to write a program. Ok, very lost. Don't worry, that is normal - unless you spend a lot of time describing your pets in actionscript, you will have this problem. So from this point on, we will only build programs that solve real problems. The downside of this is the samples will become a bit more complex. The upside? You may never again have make a Dog class and give it a haircut. We will start with what many in the programming world consider a hypothetical example, Shapes, Circles and Squares. Of course for people who use Flash, this is probably the most real, fundamental thing in the world. So we will start there, and slowly build it into a complete system, powerful enough to create full blown applications. Ok, full blown applications on a bad hair day, but in Flash. In spite of the limitations, there are many things you are able to do as a Flash programmer, that are almost impossible for other programmers. Like draw. Ha, had to slip that in. So let's begin with the very real, very useful Shape example. The Process Writers are damn liars. No, I'm not talking about the way they mislead their landlords, or lie about their education, it is worse. Here is what they do - they stay up all night, for weeks at a time, trying to figure out how to do something terribly complex, maybe a nifty oop structure or something. Finally they get it down to a few classes, a neat hierarchy, and a hundred or so lines of code. It looks great. Then in their writing, they casually mention that if you want to solve problem X, you should of course 'use this structure' because it is clearly the most robust. Or maybe not even mention that, just present the fine looking code. Everyone thinks they are geniuses, off they go on a speaking tour, and you are left feeling inadequate with your class structures that are never right the first time, or even the next time. The worst thing about it though, is you are left with the solution to problem X, and no idea how to solve problems. And you feel like a turd. One thing you can take comfort in though, is the writer is also, very secretly, feeling like a turd. All those other writers and programmers can just whip off a great structures in their sleep, while he or she has to stay up late for weeks in a sea of crumpled paper, iteration after iteration... As a result, their overwhelming feelings of inadequacy make them social outcasts, only politely tolerated by their peers. The tweed doesn't help the situation either. The point here is that designing an OO program is an iterative process. You make a structure, and it sucks. You throw it out and make another, it doesn't suck quite as much, but this part needs to go, and that property should probably higher up, those need to be subclassed. Then all at once, before your eyes, it sucks again. But you are much smarter about it now, so you recreate big pieces of it, with lots of clipboard inheritance, and more adjusting, then finally it works great, and its very neat. "Ha! I should write a book", you think. Eventually you get smart about it, and do a lot of designing on paper, a lot of thinking in advance. This may or may not save you time, but there is much less chance another programmer will find one of those awful prototype ideas and laugh at you. Not convinced? Here is a quote from Bertrand Meyer, creator of the Eiffel language, and all around super genius OO smart-guy: "..you don't need iterations, its easy..." (find that quote!) Umm, OK, well maybe people at his level don't need to do that kind of thing... Of course, he's written a number of very thick books, and even written a programming language. How can you trust a person like that to tell you the truth? There are a number of systematic processes you can use to help you figure out what the structure of your program should be, and we will touch on some of those later. More though, the applications we make will be adjusted and modified as we go, somewhat simulating things that always seem to 'come up' during the process of creating OO applications (so be sure to read the whole thing!). Hopefully this will help you understand the process and the flexibility of OO design, in spite of exposing me as a fraud (ed. I'm canceling that speaking tour!). Real Class What we are working towards here is a simple program that can be used to display and manipulate shapes. Even though the design process usually involves many iterations and abandoned models, you usually try to define the problem as clearly as you can before starting. However, to keep things from getting terribly overwhelming on page one, we will just build and add as we go. Pain is knowledge. So if our ultimate goal is a shape manipulation program, we should probably start with a Shape class. At the most basic level, we want to give shapes the ability to move and change size. In pseudo code (rough code that is easy for people to understand, but would choke a computer to death), it would look something like this: // Shape Class From this we can see that we could use it like: There are a few problems with this though. Here is a very important point to remember forever, or get a tattoo if you need: When designing classes, you want to give the user of the classes the simplest, most consistent, and unambiguous experience possible. Even if the end user is you, that holds. When the time comes to use classes, you will have other things on your mind that you won't be looking for extra challenges. Other users of your class do not have the luxury of (or burden!) stepping inside your head. So even though the code compiles, and works, it can still be quite 'wrong'. A good place to start looking for problems then is by imagining you are using the class. Let's take it one line at a time. What is the problem with the first line? s1 = new Shape( 10,20,30,40 ); The first obvious thing to say, is what do all those numbers mean? Sure we can look that up, but remember very large part of your program will go between those two sections. An what happens if we add other options - rotation, color, skew, name... Another question, what if we want to initialize the width and height, but leave the first two parameters, x and y, at their default values? Even if the user passes null for those spaces, it will evaluate to 0, which may not be the default value. The other option, just passing the width and height alone, has a problem too. How is the Shape class supposed to tell the difference between when you specify the width and height, as opposed to the x and y values. They both are just a pair of integers after all. Standing on the corner of Lafyette, wondering what's a city boy to do? First a bit of history (and you thought this topic couldn't possibly get more boring!). It used to be, as in Windows API, that you would see giant numbers of parameters and you programmed with a few thick books on your lap. Something like this: w = CreateWindow ("button","Click Me",WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, button1 = new Button (); If nothing else, the first example should go a long way towards explaining why nuclear rectors do not run on Windows 3.1. There are a few lessons here if we look a little closer though. First, only give the constructor parameters it needs to construct itself, unless you have a pretty good reason to do otherwise. It might be tempting to allow the user to pass the Text as an argument, but why is the Text more important than the location, and if passing both, which should be first? More importantly, will passing these things as parameters allow you to do anything that can't be done otherwise (other than confuse people?). The short answer is, 'Nope'. The long answer is, 'No it will not'. So then, rule of thumb, unless a parameter is needed to construct an object, don't pass it to the constructor. (Later on we will look at constructor overloading, a common though somewhat contrversial technique, that addresses the above problem in a different way). Before going further, let's take another look at our own code, the second line: s1.Move( 20, 30 ); Firstly, what does it mean? Does it mean move to the location 20, 30, or does it mean move 20 to the right and 30 down. The only way to be sure is go check the class, which I urge you to do now. Ok, now that you found it, and found your way back here (and forgot what you were doing), you realize that it wasn't defined in the above code. Before crying foul though, remember that most programs have more than one class and two methods, and most methods require more than a glance to tell you what they do. Even if they are well commented (ha!), your brain has to do a 'save to disk', then a 'search', then a 'parse and save to disk', and lastly a 'search for original location'. If your 'save to disk' works like mine, you will have forgotten why you were moving the button in the first place. Even if you have a beautiful photogenic memory, it probably doesn't go much beyond 7 items for 7 seconds. So ambiguous names just have to go. In fact that was supposed to just 'Move' the button to a new location,
not a relative location. How could I possibly know that? Because I wrote
it. So now I'll rewrite it, so you too can know. The term of choice
seems to be settling on the word 'Location', so let's go with that. Ok, that is still really awful, but at least we know what it means. You might be asking, why not just say: s1._x=20; Well, that is a really good question, with an easy, albeit long, answer. Kind of like, "When does this chapter actually begin?", except it has a real answer. Getters and Setters myButton._x=20, myButton._y=20; Pixels that is. First problem, and you might laugh, but what if you later want to use this button on a map who's coordinates are in longitude and latitude? (canned laughter) Ok, what about on a tile based game, where things are measured in tile[x,y] instead of pixels? Oh, jeeze, a game, yeah, that is a serious problem then... Maybe you are still not convinced - indeed you could probably work something out. It does get worse though. What about if, under no circumstances, did you want a button to be placed outside the screen? Of course you wouldn't do such a thing in the first place, but what about that person that sits at your desk Monday morning? You can not trust the person who uses your classes to do the right thing. They are, to a one, evil twisted plotting malicious bastards and bastardettes. In their defense it also can get a bit complicated, even on a Thursday after coffee, when nested windows are resizing. So rather than check that 'x' is a correct, legal value every time you set it, why not give yourself a little protection? Have a method set x, and have it do the right thing. One more reason, for good measure - it isn't inconceivable that your button could have a friend that is interested it it's location. For example a shadow. It probably whould be a separate object from your button (button one shouldn't cast a shadow on button two if they are touching, so different levels have to be involved at very least). If you 'set' x in a method rather than assign it, you can let the shadow know at the same time. There are many cases beyond shadows where you might want other clips involved in a single 'object', so it is good to keep your options open when setting things like the location. Ok, that covers some of the nasty things that may happen if you are in the habit of setting values directly, the stick if you will. There are carrots too though. For example, how do you make a value read-only in actionscript? You make a mental note not to set it, and if it's really important, use a post it note (or perhaps 'another' post it note with a darker pen) on the side of your monitor. If you are getting and setting that value through 'getter/setter' methods however, it is easy - just don't make a method that sets the value (or make one that traces an error message if you prefer). Another nice thing is, you are not obliged to have a one to one relation with your data and methods. Instead of saying setX and setY, we can say setLocation, or indeed all three. Instead of saying setWidth and setHeight we can say setSize. Sure, inside our class somewhere there exists an x, y, width, and height, but the user doesn't have to care about that. It goes the other way too, you can have more than one method to set the same data. You can have a method called setRectangle that sets all four fields in one shot. Internally it might call both setLocation and setSize, or it might set x, y, width, and height one by one - the users of your class are left oblivious to what is going on, which is their natural state. These are all arguments for using what is known as 'getters and setters'. The idea is that you have a property inside your class that can only be manipulated via methods. These methods have historically had names like getXX() and setXX(), thus the term 'getters and setters'. You should use them unless you are sure that the value in question will never have to be validated, restricted, enhanced or otherwise 'massaged', and you are also sure that the value will never be mapped to something different. If you are too lazy, just force yourself a few times and soon it will seem natural. You can easily get in the habit if you make a point of doing it just after you comment your code. Hrrmm... <sidebar> button1.Location = new Point (168, 168); That certainly doesn't look like a setter
to me, right? Well, actually that is what is known as a 'property' (yeesh,
more confusion) in C#, and it automatically calls a get method when
reading the value, and a set method when setting it. Microsoft likes
them so much they built them into the C# language, which they have a
monopoly on by the way, wiping out the need to ever use a 'getXX' or
'setXX' method. Call the DOJ. Well, no such luxury in Actionscript,
so we'll have to just manually name the methods 'getXX' and 'setXX'.
If it's any comfort, they have very average vector graphics support. Ok, perhaps with all this we should make a second attempt at the Shape class. Maybe with a little less pseudo, and a little more code: // Shape Class // Properties // Methods You sure don't have to write much code before a problem pops up. The problem of course is with getSize - there is no way to return from a method twice, and yet we have two things we have to return. The most obvious solution is to wrap the two values in an object, and return the object - like obj.x and obj.y. This would get both values back, but how can we let the user of our class know to expect this? We can go the extra step, and instead of just returning 'an' object, we create a standard object that is always used. This will turn a liability into a feature, a very important skill for both software developers, and software marketers. What is the fundamental 'unit' of a location? Looking at the different ways it can be measured, x and y, longtitude and latitude, xyz, or even angle-radius, they all describe a Point. Our program will be in 2D, and use cartesian coordinates, so our Point should contain an x and a y. As you probably have guessed, we will define this point with a class: // Point Class and when we set our location, we can now say: s1.setLocation( new Point(20, 30) ); Besides solving our 'get' problem, this offers a number of great advantages. The first is that our user's code is much clearer. With only that line to look at, it is still pretty clear that the location of s1 is being set to a point. Next, the setLocation method is now sure of what it will be getting - this isn't such a big thing with just x and y, but when you have more complex objects it can be very helpful to know that all values will be set to defaults if not set explicitly. It also makes copying properties from one object to another very simple: s2.setLocation( s1.getLocation ); Sure you could have done that with two statements, but this frees you from having to get into the details. Imagine if you were copying the Rectangle rather than the Location, that would be four statements. You can also have a get and set State method, that copies the entire state of something like a movieclip (x,y,rotation,alpha...). As you can see this offers a few pretty solid benefits, but does this stop us from having a setX or setY? And if the location is stored in a Point object, how does the rectangle get stored in a Rectangle object - are there two versions? The great thing is, everything still 'resolves' to x, y, width and height. When you set the location, you pull the x and y out of the Point object, and assign them to x and y. The width and height, passed in a Size object, still in the end sets the width and height. The Rectangle, which sets all four, can either go through the Location and Size methods, or set all four itself. And yes, you can still have setX and getY methods. They work something like this: // Methods Enough pseudo code though, here is the real thing. Or perhaps, the real 'hardly any-' thing. <fla> Well, that just might do it for our first step. It may seem like a lot of code for how little it does, but the beauty of OO is you will probably only write this once in your whole program. It will get tucked away in some higher class, and just 'work' when you call it. The even nicer thing is, you might only write this once in your life. Ha! OK, that is a wild exaggeration added to an utter lie, but it is a pretty simple thing to just reuse a class in another project - just hook it in, et voila. Of course we are probably going to modify it very shortly, certainly add to it. Why let the truth spoil the concept though... Now for the subClasses A Real Subclass If this were a computer science class, we would now create the subclass's Circle and Square, they both would have a getArea method, and each would return the correct area calculated in a completely different way. Indeed, this is a nifty, if somewhat useless demonstartion of how OO allows you to use the same interface for different objects. The big thing is that the 'guts' are hidden from you, you just trust each to give you the correct area. This is known as encapsulation, or if you prefer, 'I don't care how you do it, just get it done'. Maybe you had that person for a boss once - if you remove the temper though, it is not a bad way of working. The 'boss' doesn't have to be a graphic designer or programmer to ask you to do such a task, and you are trusted to use your best judgement and the expertise that got you hired in the first place. You can imagine that your boss, who failed 'crayons' in kindergarten, isn't the person to give you color advice. Likewise, running a compnay into the ground isn't your area of expertise. A good separation allows you both to do what you are best at, without being distracted by, or polluting the others domain. And of course if you can't get it done, you get fired - don't be afraid to apply the same standards to the classes you make and use. The idea of encapsulation, or hiding information that isn't relevant to the user, should always be on your mind when designing classes. You want to tell the user of your class everything they need to know, and nothing they don't need to know. More importantly, you never want to force them to know a detail like how you calculated area before they can use your class. All they need to know is that the area is returned when they call the getArea method, no matter what type of shape they are using. So that sounds fine, but I've often wondered just what program in the world runs around calculating areas of simple shapes. Perhaps one day, like a lot of useless math, a whole universe will be discovered that is based on exactly this (populated by dogs obsessed with counting their legs, no doubt). For now we'll skip the area calculation, but we will make a mental note to keep an eye on the science section of the newspaper. We will create two subclasses, Circle and Square, that we've decided, but what makes them unique enough from Shape (and each other), to require separate subclasses? The most obvious thing is that they look different, but that ends up being a bit of a hard to pin down argument. After all, a red square looks different than a blue one, and a circular mask looks like whatever is underneath it. In fact, so far our shape doesn't look like anything, and it is very conceivable that our circle and sqaure class won't either. Certainly we can imagine a Circle class that only deals with formulas. Maybe you aren't convinced - fair enough, a Circle class does seem pretty bound to what a circle looks like. What about then, if we have another Shape subclass called 'polygon'? Can the idea of a polygon be defined by an image? What do 'polygons' look like? Of course they don't have to 'look like' anything, they just have to fall within a certain sets of 'mathmatical constraints'. Computers, of course, love this. So we just need to define the 'mathematical constraints' of our circles and squares - don't worry, we will stick to the fifth grade math book. Ok, we know that circles have a center point and a radius, squares have a length and a width. *Crash!* That was the sound of reality crashing into our theory. No matter how much of a programmer you are, you've probaby made both a circle and square in Flash. You know that they will have an x and y location, and a width and height. You can check the info box if you like X, Y, W, H. Even for circles. So the question is, where is the radius? The truth is, it doesn't work like that in Flash, as you know. Circles fit inside the box, just like squares, just they don't do a very good job of filling up the corners. They have a width and height, and their X and Y properties represent the top left corner of the 'bounding box'. This brings up another overloooked peice of reality, both our Circles and Squares will have a width AND height, inherited from Shape. If you still can recall the fifth grade however, you'll know those have to be the same in both a circle and a square. This is a great chance to use a super OO move - override the setWidth method, and have it automatically set the height too, and vice versa. setSize can be overridden to, and just ignore the second parameter. Probably these two can inherit from Rectangle and Ellipse, being they are specialized types of those (a square 'is a' rectangle). Perfect. Well, have a drink - it will help us ignore that feeling in the back of our head that something is wrong here. The 'skillful' use of overridden methods seems fine enough, there is something bigger though. Why exactly are we subclassing shape again? We want to make a little program that creates shapes, and lets us drag them around. Certainly we want different types of shapes, and circles and squares seems like a logical starting point. Yet, when we look closely, aren't circles and squares pretty much the same thing to Flash - movieclips with graphic data in them? More significantly, do we care if circles have a radius - will we ever need to know that? Certainly not to paint it pixel by pixel. Probably for hitTesting, but we could also use buttons, the hitTest property, or even some dropTarget fanciness for that. Let's not rule that one out, but is there anything else? When faced with these moments of nervousness and doubt, it is always good to fall back to very the basics. "You are human, and your purpose on earth is to propogate your genes." Ok, maybe not quite that basic... As a builder of classes, the basics are 'what would make the (dickhead) user of these classes happiest'. As a builder of the application (using classes), the basics are 'what would make the (very dickheaded) user of the application happiest'. As the application end user, the basics are 'rub sticks, make fire'. Right now, we are the 'builder of classes', so what would the user of these classes want? A good excerise to help you figure that out is just imagine it is done, and it is perfect - and write some code that uses it: shape1 = new Polygon ();
So there we go, we've let the user of our class answer our question. Even if the circle class doesn't do anything different that a square class (other than cause a circular movieclip to be displayed), our user thinks of them as different things, so we may still need the class. Of course our user is probably 'thinking' ellipse when saying circle (sneer). In fact look, right after using it, they size it to be an ellipse! This brings up a dilemma. Do we use the incorrect term to accommodate the fact that users of classes call ellipses circles, rectangles squares, and recorders flutes? Or do we take the high road, use the correct term, and have the users curse us everytime they use 'circle' when meaning 'ellipse', or everytime they spell 'ellipse' wrong? We take the high road of course. If we call it circle, it would mislead anyone who knew better into thinking they couldn't turn it into an ellipse. Besides, no one ever got fired for using the correct word. That being said, avoid class names like 'Embezzlement' in your financial applications too. So then, lets make two subclasses of Shape for now, Ellipse and Rectangle, because they are easy. In fact, for now at least, it looks like they won't be doing anything other than making a shape and remembering their own names (we are intentionally delaying dealing with movieclips here, as you might have noticed). Ellipse = function(){} Rectangle = function(){} Before we get into polygons, can you see a problem with the above code? The code itself looks fine (other than the horrid syntax we have to use with flash 5 to create inheritance!), but doesn't 'Rectangle' sound familiar? Didn't we also set the xywh of an object using a rectangle before? Yes we did, or at least we talked about it. There already is a class called Rectangle, that is completly unrealated to this new one. This is a problem, because one of them will overwrite the other (_root.Rectangle and _root.Rectangle!). It is actaully good that we came across this problem right now, before having written miles of code. Also, the next section is about Namespaces, which deals with exactly this problem. What a coincidence... Namespaces Classes of course, are just recipes for creating objects. The object is the chiffon pie, the class is the recipe used to make it. You may store your favorite recipes on little cards tucked away in a little card case (the computer revolution has no place in the kitchen!). Probably you have at most one recipe for chiffon pie, but then you might have quite a few for chocolate cake. The favorite one is always easy to find, because it is covered in dried chocolate cake batter, but if you have twelve? They are all chocolate cake, and even looking over the ingredients (inspecting the classes) doesn't easily let you sort them out. Most likely, at first anyway, you will 'tag' them with extra information. "Grandma's Chocolate Cake", "Julia Child's Chocolate Cake", "Sugarless Chocolate Cake"... At the most basic level, this is why you use namespaces - to tell things apart when they have the same name. However, as your grandmother probably told you, behind every burden lies an opportunity to structure class libraries in a way that avoids namespace pollution and facilitates reuse. Bless the dear. If we look at our Rectangle problem, we have a class that 'mathematically' describes the dimensions of a rectangle (x,y,w,h) as opposed to a point, and another that describes a 'Rectangle' shape object (that might be blue), as opposed to an ellipse or triangle. So we clearly have two different things, and if they have the same name we will certainly have problems. We could just rename one, but that isn't always possible - what if we are using someone else's classes, or even two of our own classes from previous projects? What if we are following a spec, and the spec says 'rectangle'? Worse, what if we don't notice, and we can't figure out why things get funny once in a while? If you use namespaces, then not only do you solve these problems, but you have the opportunity to organize where your classes are defined into a nice logical structure, making them easy to find an use (and reuse). So why doesn't everyone use namespaces all the time? Mostly, they do. Namespaces, in a nutshell, are taking a group of related classes, and putting them in a box. Going back to the recipes, you would have separate boxes for groups of recipes. Of course not one box for chocolate cake recipes! One for Grandma's, one for Julia's.. As you can guess, boxes are objects, and boxes can contain other boxes. So once we decide that we want to 'cook like Grandma', we open up 'Grandma's recipe box', and perhaps find little boxes like 'pastry', 'pates', and 'puddings'. Namespaces are just the same. Rather than defining all your classes in _root or _level0, you define them inside a separate object. That could be _level0.Grandma.Cake.ChocolateCake, rather than _level0.ChoclateCake. While we can define these 'namespaces' in the timeline, there is a lot going on there too - there are movieclips being attached, movies being loaded, and even properties being accidentally being defined there (when you forget to use 'var'), so it isn't really a safe place. It would be easy enough to just make sure you don't overwrite _level0 when loading a swf, but remember if you want others to use your classes, that becomes a requirement they have to know about. The 'safe' place to put things is in the Object namespace, because it is not associated with a timeline. Lets drop for a moment the imaginary world of Grandma's cooking, and come back to the reality of defining classes in namespaces. Normally you choose your own top level Namespace, that all your class definitons go into. If you work for a company, the company name is probably a good choice (as long as you don't work for 'Object.prototype' that is). If not, then you should probably invest in a nice pair of clothes, a haircut, and a weekend paper. You can use your own full name in the meantime. It really doesn't matter what you use, as long as you always use it, and no one else uses it. Java usually uses the reverse order of your company's domain name, which is certain to be globally unique (Note, in Java, namespaces are called 'packages'). That is fine too, as long as you don't change your domain too often, or merge with another domain, or have many domains - if that is the case you could just pick a domain and stick with it. With a system like this, if you now want to use Sparkysoft's menu system, and Aquamedia's button classes, you won't end up with two definitons (in the same place) for a class like 'container'. Also, the classes you just wrote will know where to find the classes you wrote six months ago. With our top level namespace implemented, we can feel good about sharing our classes with others, however we still have the 'Rectangle' problem. So within our own namespace, we now can create categories for the different types of functionality we implement. Things like 'Cake' and 'Soup'. It probably helps to look around at some other namespaces to get a feel for it, but of course this is part art part science too. Here are some sample namespaces (and packages) to start with: <get examples>
Now in a perfect world, one might think that every class would be pure and wonderful enough to belong in the framework. However, most classes are created to just do a bit of heavy lifting and then go queitly into the night. That is not to say they should just be defined in _root and given some unglamourous name - they too need to be safe from name collisions or being overwritten by a loaded movie. So these classes also will go in a namespace, and will also end up being defined in the Object namespace, just they will be separate from the framework. In fact even your instances may also end up being created somewhere in the Object namespace, leaving the timeline free of everything but movieclips. Of course in 'regular' non-Flash programming you do not have the problem of movies being loaded into the space you write your code because there is no timeline, everything is automatically in a 'safe' place. In Flash however, you are always dealing with two 'scopes', inside an object and inside a timeline. That is why you so often see the word 'this' in Flash programs - meaning "inside 'this' object", rather that the default current timeline. That is also why, even though everything must be written in a timeline, you can still define it to go in the Object namespace. If you are still unsure why this would be useful, imagine loading a class from an external movie. If the class is defined in the '_root' of the movie, the code you use to create instance of the class will be dependant on the name and path of the loaded movie - probably different every time. However, if the loaded movie just dumps a class definiton somewhere in the Object namespace, you can create instances of it the same way everytime, making reuse much easier (as well as debugging!). And of course you are sure that no matter how many movies you load in this way, you will never have one name conflict with another. So what is the bad news? The bad news. The bad news can probably be summed up in three words - 'carpal tunnel syndrom'. Unless you have a secret fettish for typing long prefixes to classes, you are probably going to find the route to your classes a bit distressing in Actionscript... Speaking of which, my hands hurt. Bye for now!
|
