Requirements

Prerequisite knowledge

This article is designed for intermediate ActionScript developers. An understanding of ActionScript 3 fundamentals (syntax, variables, functions, conditionals, loops, arrays, and the like) and of how to work with objects in ActionScript 3 is required.

User level

Intermediate

You already know that ActionScript 3 is an object-oriented language. Almost everything you will need to build an application using ActionScript 3 is an object—Sprite, MovieClip, TextField, Array, and so on. You can create interesting and complex applications and games using only the classes that are part of ActionScript 3. But your programming possibilities become endless when you learn how to design and create your own objects that are specific and potentially unique to your needs.

All objects, whether those core to ActionScript 3 or the ones you create yourself, come from a class. In this article you will learn the basics of writing a class. You will also be introduced to some of the goals of objected-oriented programming that you need to keep in mind when designing the objects you will use. These concepts include the Single Responsibility Principle, reusability, flexibility, and encapsulation.

Writing a class

A class is a blueprint or template or set of instructions to build a specific type of object. Every object is built from a class. Remember the chocolate cake example in Object-oriented programming concepts: Objects and classes? A class is like a recipe. You can’t eat the recipe but you can eat the instance of the recipe that is a result of following the recipe instructions. Look at how a chocolate cake class might appear in code:

package cakes { public class ChocolateCake { public var numberOfCandles:int; public function ChocolateCake(){ } public function putCandlesOnCake():void{ trace("Putting " + numberOfCandles + " candles on the cake."); } } }

In ActionScript 3, all classes must have three pieces: a package statement, a class declaration, and a class body with a constructor method. Errors will occur if any of these pieces is missing or improperly coded.

Package statement

package cakes {…}

The first line of a class tells the compiler what package the class belongs to. Packages organize your classes into groups and are used by the compiler to create a unique, fully qualified name (Review ActionScript 3 fundamentals: Packages for more information about working with packages). In this example, the class is in the cakes package.

Class declaration

The second line of code in the example above,

public class ChocolateCake {…}

is the class declaration statement. The class keyword tells the compiler that a class is being defined. It is preceded by an attribute keyword (public in this example) and followed by the class name (ChocolateCake).

Class attributes are similar to the access control attributes used for variables and functions. When part of the class declaration, the attributes modify the entire class.

Table 1. Class attributes

Attribute

Definition

dynamic

Allow properties to be added to instances at runtime.

final

Must not be extended by another class.

internal (default)

Visible to references inside the current package.

public

Visible to references everywhere.

Note: Abstract classes are not supported in ActionScript 3. Therefore, there is no abstract keyword. Also, the private and protected attributes cannot be applied to classes. They only have meaning inside of a class definition.

Class names follow similar naming conventions as variables in ActionScript 3. The main difference to note is that class names always start with a capital letter. The name of the class should be descriptive. ChocolateCake, ProjectStatus, ProjectClient, CustomButton, ShoppingCartItem, and ProgramPiece are all examples of good class names.

The name of the source file must be the same as the class with the .as file extension appended. For example, the file that defines this ChocolateCake class must be named ChocolateCake.as.

The class declaration statement will also include the keyword extends followed by a class name or implements followed by an interface name if inheritance or an interface is being used. 

Class body with a constructor method

All of the code that is contained within the curly brackets that follow the class declaration is considered the class body. Remember, objects are self-contained components that contain properties and methods needed to make a certain type of data useful. The class body contains those properties and methods. Here is the class body for the ChocolateCake example:

public var numberOfCandles:int; public function ChocolateCake(){ } public function putCandlesOnCake():void{ trace("Putting " + numberOfCandles + " candles on the cake."); }

In code, properties are variables declared within a class. The ChocolateCake class only has one property: numberOfCandles. Methods are functions defined within a class. ChocolateCake has two methods:  putCandlesOnCake() and the constructor method ChocolateCake().

Every class has a constructor method. The constructor method is a method that is automatically called upon instantiation. When you use the new keyword to create a new instance of a class, you are calling the constructor method of the class. Any code included in the constructor method is executed every time the class is instantiated. There are a few things to remember about the constructor method. First, it is always public. ActionScript 3 does not allow constructors to be anything but public. You cannot use any other access control attribute keywords. Also, you cannot use a user-defined namespace with a constructor method. Second, the name of the constructor method must be the same name as the class. In this example, the class name is ChocolateCake so the name of the constructor method is also ChocolateCake. Finally, the constructor cannot have a declared return type as other functions do. The constructor method returns an instance of the class upon instantiation. The compiler implicitly understands that the return type is the class it is constructing.

Instance versus static properties and methods

Objects instantiated from the same class will have the same properties and methods. Continuing with the chocolate cake example, every instance of the ChocolateCake class will have a numberOfCandles property and a putCandlesOnCake() method. However, the value stored within the numberOfCandles property can vary for each instance. If every instance of the class had the same value for the property, every cake would have the same number of candles every time you baked it. That would not be very useful over a long period of time. Properties that vary for each instance are called instance properties.

Instance methods provide functionality that is useful for each instance of the class. Because putCandlesOnCake() is an instance method, each instance of the ChocolateCake class can have candles on it. If you don’t want candles on a specific instance, you wouldn’t call the putCandlesOnCake() method for that instance.

Instance properties and methods are accessed by using dot syntax with the reference variable name and the name of the property or method. If you instantiated the ChocolateCake class and assigned it to a reference variable named myCake, you would use myCake.numberOfCandles and myCake.putCandlesOnCake() to access the instance property and method of the class.

You will notice the this keyword in the code below.  The this keyword is used to reference the instance itself. It can be used to reference anything defined at the class level. In the example below, it is being used to reference an instance property.

Static properties and methods belong to the class rather than an instance of the class. Static properties are properties that describe the class, not just a specific instance. In a revised version of the ChocolateCake class below, the property flavor is a static property. This property will tell you what flavor the recipe is without having to go through the process of instantiating.

package cakes { public class ChocolateCake { public static var flavor:String = "Chocolate"; public var numberOfCandles:int; public function ChocolateCake(candles:int){ this.numberOfCandles = candles; } public function putCandlesOnCake():void{ trace("Putting " + numberOfCandles + " candles on the cake."); } } }

Similarly, static methods encapsulate functionality that does not affect individual instances of the class. It shouldn’t make sense to apply the functionality in a static method to an instance of the class. Leaving the ChocolateCake class for a moment, consider the Date class of ActionScript 3. It has a static method named parse(), which takes a string and converts it to a number. The method is static because it does not affect an individual instance of the class. Instead, the parse() method takes a string that represents a date value, parses the string, and returns a number in a format compatible with the internal representation of a Date object. This method is not an instance method, because it does not make sense to apply the method to an instance of the Date class.

Contrast the static parse() method with one of the instance methods of the Date class, such as getMonth().The getMonth() ;method is an instance method, because it operates directly on the value of an instance by retrieving a specific component, the month, of a Date instance.

Static properties and methods are declared by using the static keyword. Because they belong to the class, they are accessed using the class name rather than a reference variable. The static property for the chocolate cake class would be accessed by ChocolateCake.flavor. The static parse() method of the Date class mentioned above would be accessed by Date.parse().

SRP, reusability, and encapsulation

Some of the principles of object-oriented programming are principles of class design. Using well-designed classes helps to increase the stability and maintainability of your application. Well-designed objects only have a single responsibility and are reusable, flexible, and encapsulated.

The Single Responsibility Principle (SRP) states that a class should never have more than one responsibility. If it does have multiple responsibilities, changes to one responsibility may unintentionally break the other responsibilities in the class. This can lead to a domino effect in your application, causing it to perform or break in unpredictable ways.

When designing objects, keep reusability and flexibility in mind. You want to write classes that can be used over and over again within your application, and possibly in future ones. Ask yourself if the object you are designing can be used in the next version of the application with few if any changes. Can it work if the application is reconfigured? Can it work in another application? You will be able to answer yes to at least one of these questions if you are building a reusable, flexible object. When you begin using the principle of customization through inheritance, your objects can achieve an even higher degree of reusability.

In the real world, objects frequently hide their information and how they work. They have a public interface that allows you to successfully use the object without knowing anything about how it works. You don’t need to know the internal details in order to use the object as long as you know how to use its public interface. Think about a light switch. In order to turn on the lights, you simply toggle the switch. You don’t need to be an electrician or understand anything about electricity to work the electrical system in a room. An automobile is another good example. As long as you know how to use the gas and brake pedals, steering wheel, and ignition, you can drive. You don’t have to understand how the engine, or transmission, or electrical system works to drive a car. Nor do you need to know the specific properties of the car such as engine size, horsepower, or torque. In object-oriented programming, well-designed objects encapsulate their data and functionality from other objects. You can learn more about encapsulation in Object-oriented programming concepts: Encapsulation.

Below are three versions of a rectangle class designed to draw a rectangle. Each version of the class brings it closer to achieving the goals of SRP, reusability, flexibility, and encapsulation.

Rectangle class version one

Look at the class MyRectangle_v1 in the code below. It has a single responsibility—to draw a rectangle. Every instance of this class will draw a rectangle when its draw() method is called. Because literal data is used in the draw method for color, size, and location, the rectangle drawn will always be the same size, the same color, and at the same location. Although the class does have a single responsibility, the fact that every instance will draw an identical rectangle reduces the reusability and flexibility of the code.

package { import flash.display.Sprite; public class MyRectangle_v1 extends Sprite { public function MyRectangle_v1() { } public function draw():void{ graphics.lineStyle(1); graphics.beginFill(0xff0000); graphics.drawRect(100, 100, 75, 25); graphics.endFill(); } } }

Rectangle class version two

One way to make the class more reusable and flexible is by adding parameters to the draw() method for the rectangle’s size, color, and location. By adding these parameters, the rectangle’s size, color, and location can be varied each time the draw() method is called.

package { import flash.display.Sprite; public class MyRectangle_v2 extends Sprite { public function MyRectangle_v2() { } public function draw(outlineWeight:Number, color:uint, xLocation:int, yLocation:int, width:int, height:int):void{ graphics.lineStyle(outlineWeight); graphics.beginFill(color); graphics.drawRect(xLocation, yLocation, width, height); graphics.endFill(); } } }

Rectangle class version three

This rectangle class is reusable, flexible, and has a single responsibility—three of the goals of object-oriented programming. In the final version, look at the first step toward encapsulating the important properties of the rectangle. To encapsulate the properties from other classes you use the keyword private during variable declaration. This is only the first step towards encapsulation. There is more you have to do to fully encapsulate the object. The concept and process of encapsulation will be covered in detail in Object-oriented programming concepts: Encapsulation.

Private properties are still accessible from within the class itself. Below, values passed in as arguments to the constructor method are assigned to the private properties. Those properties are used as arguments for the method calls within the draw() method.

package { import flash.display.Sprite; public class MyRectangle_v3 extends Sprite { private var _outlineWeight:Number; private var _color:uint; private var _xLocation:int; private var _yLocation:int; private var _rectangleWidth:int; private var _rectangleHeight:int; public function MyRectangle_v3(outlineWeight:Number, color:uint, xLocation:int, yLocation:int, rectangleWidth:int, rectangleHeight:int) { _outlineWeight = outlineWeight; _color = color; _xLocation = xLocation; _yLocation = yLocation; _rectangleWidth = rectangleWidth; _rectangleHeight = rectangleHeight; } public function draw():void{ graphics.lineStyle(_outlineWeight); graphics.beginFill(_color); graphics.drawRect(_xLocation, _yLocation, _rectangleWidth, _rectangleHeight); graphics.endFill(); } } }

Where to go from here

Being able to write classes expands the possibilities you have while developing an application. Making sure those classes are well-designed according to some of the principles of object-oriented programming helps to increase the stability and maintainability of your application. You now know the basics of both of these important skills. You should continue learning more about object-oriented programming concepts in ActionScript 3 by reading about encapsulation and composition.