Thursday, June 25, 2009

Encapsulation


Now let's move on to another important principle of OOP called encapsulation. The underlying concept of encapsulation deals with what methods and properties your class exposes to the outside world.

Up until this point you've always set methods and properties as public, but is that really what you'd want to do?

If you want your code to be stable, and if you want to develop projects that are least prone to bug and errors, you'll want to restrict the ways in which other classes can interact with your code.

The key here is to only have those methods and properties available that are required as settings for the class, and restrict access to the rest. Using this approach, you'll have a limited number of places in your code to debug when something goes wrong in the interaction with your class.

ActionScript 3.0 includes the following keywords as access modifiers to methods and properties:

  • public: allows access from anywhere
  • private: can only be accessed within its own class
  • protected: can only be accessed within own class and subclasses
  • internal: can be accessed by all classes in same package

Let's put this concept into practice. Take a look at the following example:

Person.as

package com.adobe.ooas3 {
public class Person {
private var _age:Number;

public function get age():Number {
return _age;
}

public function set age(val:Number):Boolean {
if(_age < _age =" val;">

The code example above shows a Person class with a private property _age of a Number type. Even though you want to allow the age of the person to be set, you still opted not to use a public property here. Instead you are routing the age value through the use of a getter/setter method.

Getter/setter methods (as implemented in the previous example) appear as if they are properties, but they behave like methods.

When you set the age property, it calls the age setter method that assigns the value to the private _age property after validating it. In this case, you know that a person's age can't be a negative value. If the given value is valid, the setter method returns true, or else returns false.

The major advantage of this approach is that every time the age property is set, validation occurs because the age value is always routed through the setter function. If the age property were set as public, every method in your class that wants to use the age property would first need to check if its current value is valid.

classes

What are classes?

Classes are nothing more than a collection of functions (called methods in this context) that provide a blueprint for any number of instances that are created from it. By changing some variables (or in OOP terminology, properties) of a class instance, or by passing different values as arguments to its methods, the same underlying class can have widely different outcomes.

Another way of understanding a class is to compare the tasks performed in the Flash authoring environment. While ActionScript classes are created programmatically, they are basically analogous to the concept of importing assets and then working with symbols in the Library panel and the symbol copies (instances) on the Stage. The symbol instances on the Stage are similar to class instances. The different instances on the Stage—despite coming from the same symbol—can each individually have different properties settings that affect how they appear (such as color, alpha transparency, rotation, and so on).

The ability to generate multiple instances (that can appear and behave differently) from the same object is one of the reasons why classes are so powerful. An ActionScript class is even more powerful than a symbol in the Library because, as a developer, you have complete control over how an object looks and behaves. There are many properties and methods accessible using ActionScript 3.0 that you cannot access through the Flash authoring interface. Additionally, ActionScript classes make it possible to dynamically animate objects during runtime that are outside the scope of timeline-based animations. Finally, writing your own ActionScript classes promotes reusability because the functionality you create can be repurposed.

Let's see how this looks in code:

Brick.as

package com.adobe.ooas3 {
public class Brick
{
public var color:String = "red";
public function Brick() {
trace("new "+ color +" brick created");
}
}
}

The Brick.as code above illustrates one of the most basic implementations of an ActionScript 3.0 class. The class holds just a single method and property. As you walk through each line of code, the following facts come to light:

  • First, a package is defined. Packages allow you to structure your code in folders on your hard drive and prevent class name conflicts. In this example, the class is saved as Brick.as (the exact case-sensitive class name), inside a folder named "ooas3", which in turn is stored in an "adobe" folder that itself is contained within a folder called "com".

    By convention, package names are usually the domain name of the project in reverse order, followed by the project name. Doing this ensures that the package name is unique and there is no risk of any other project's classes conflicting with your own.

  • Then the class is defined and set as public (more about access modifiers later). In our example the class is named "Brick".
  • The next line of code declares a public property "color" of a type String that we will be using in the class. The value of color is set to red. There are better ways of working with class properties—as you'll learn about when I discuss encapsulation.
  • Finally, the Brick method is defined. Note that this is not just any method. Every class has a constructor, which is a method that is automatically called when a new instance of the class is created.

    You can easily recognize constructor methods because they always use the same name as the class within which it is defined. Knowing this, you can easily see that, for the Brick class, this Brick method is its constructor.

    Inside the method we are tracing out a message to the Output panel that reads: "new red brick created". The trace statement incorporates the value of the variable color, which we defined with a default value of "red."

To quickly test this class, launch Flash CS3 Professional, create a new blank ActionScript 3.0 FLA, and write the following lines of code on the first keyframe on the main Timeline:

import com.adobe.ooas3.Brick;
var firstBrick:Brick = new Brick();

If you run Test Movie (select Control > Test Movie), you'll see that the Output panel now displays "new red brick created" (see Figure 1).

Output panel of Brick class getting instantiated

Figure 1. Output panel of Brick class getting instantiated

The traced message in the Output panel means that your ActionScript 3.0 class is functioning correctly. Now it is time to start doing some more advanced things with classes.

Wednesday, June 24, 2009

Object-oriented programming with ActionScript 3.0

Basic OOP concepts: Inheritance vs. composition

There are just a handful of concepts at the core of OOP. This article covers the most important ones: inheritance, encapsulation, and polymorphism. I also discuss a few related topics to help you put these ideas into context.

Without a doubt, inheritance is the most well-known principle of OOP. Inheritance can be defined as the ability to inherit properties and methods and extend the functionality of an existing class in a new one.

If you're thinking ahead, you might imagine creating a new "Wall" class that extends the "Brick" class you created earlier. However, that is not how inheritance works.

Looking at the relationship between a brick and a wall, the best way to code this is not through inheritance but rather by a concept called composition.

A simple rule of thumb determines whether the relationship between classes is one that warrants inheritance or composition. If you can say class A "is a" class B, you're dealing with inheritance. If you can say class A "has a" class B, the relationship is one of composition.

Here are some examples of inheritance:

  • Cat "is an" animal
  • Engineer "is an" employee
  • Rugby "is a" sport

Here are examples of composition:

  • Wall "has a" brick
  • Computer "has a" keyboard
  • School "has a" teacher

So what is the difference in how inheritance and composition are implemented? Let's compare how this works, starting with inheritance:

Animal.as

package com.adobe.ooas3 {
public class Animal {

public var furry:Boolean;
public var domestic:Boolean;

public function Animal() {
trace("new animal created");
}
}
}

The Animal.as code is the base Animal class, which you will now extend using inheritance with a Cat class:

Cat.as

package com.adobe.ooas3 {
public class Cat extends Animal {
public var family:String;
public function Cat() {
furry = true;
domestic = true;
family = "feline";
}
}
}

If you look at the Cat class, the constructor assigns values to three different properties. On close inspection, only one of these properties (family) is defined in the Cat class. The other properties (furry and domestic) come from the Animal base class.

While this not exactly the most practical example, you can see how class inheritance allows you to build upon existing functionality to create a new blueprint for you to start using as you develop your project.

Now if you wanted to create half a dozen cats, you could simply do this by instantiating the Cat class, which has all the properties already set up, rather than using the generic Animal class and having to define the properties again and again for each instance.

New in ActionScript 3.0 is an override keyword that is used when (you guessed it) you want to override a method defined in the class that you extended. This useful feature prevents you from accidentally running into naming conflicts with methods between classes that extend each other.

On the other hand, composition doesn't have any formal syntax like the extends keyword. Composition simply instantiates its own instance of any class it wants to use.

Let's take the Brick class created earlier. In this next example you'll create a Wall class that uses composition to instantiate instances of the Brick class:

Wall.as

package com.adobe.ooas3 {
import com.adobe.ooas3.Brick;
public class Wall {

public var wallWidth:uint;
public var wallHeight:uint;

public function Wall(w:uint, h:uint) {
wallWidth = w;
wallHeight = h;
build();
}
public function build():void {
for(var i:uint=0; i

In the code above, the Wall class accepts two arguments passed to its constructor, defining the width and height in bricks of the wall you want to create.

Let's do a quick test of this class by instantiating it on the main Timeline of a blank FLA file:

import com.adobe.ooas3.Wall;
var myWall:Wall = new Wall(4,4);

If you run Test Movie (Control > Test Movie), you'll see that 16 Brick instances are created with corresponding trace statements displayed in the Output panel to create a 4 x 4 wall (see Figure 2).

Output panel of Wall class getting executed

Figure 2. Output panel of Wall class getting executed

Apart from the difference in class relationship between inheritance and composition (as I discussed earlier), composition has the advantage of being able to add functionality to another class at runtime. It allows you to have control over the creation and destruction of class instances, whereas with inheritance the relationship between the classes is fixed and defined at the time the code is compiled.