ActionScript 3.0 is called
object-oriented because it supports inheritance, encapsulation and
polymorphism. The first two notions can be easily defined.
Inheritance
allows you to design a class by deriving it from an existing one. This
feature allows you to reuse existing code without doing copy and paste.
For example:
[as]//ancestor
package com.ryaco.ui{
public class ControlExt { var name:String; }
}
// descendant
package com.ryaco.ui{
public class Slider extends ControlExt {
var name:String;
}
}
// descendant
package com.ryaco.ui{
public class Grid extends ControlExt { var name:String; }
}[/as]
Encapsulation
is an ability to hide and protect data. In other words encapsulation is
what makes a code object an object. Encapsulation has been used
synonymously with other terms such as
component and
module.
In the context of OOP, encapsulation is often called
a black box, meaning you can see it do certain things but you cannot see the inner workings.
AS3
has access level qualifiers such as public, private, protected and
internal to control the access class variables and methods. For
example:
[as]package com.ryaco.ui{
public class Preloader extends MovieClip {
private var _progress:Number; //constructor public function Preloader(){
_progress = 0;
}
//public methods
public function setProgress(prgs:Number):void{
_progress = prgs; // do some other actions e.g. change progress bar width, displaying progress value in text field etc.
}
}
}[/as]
In this case we don't need to know about _progress variable, just need to know how to work with
setProgress method (i.e. which parameters we have send there).
Now we can speak about Program Design with Interfaces and Polymorphism.
To
illustrate how you can design AS3 programs with interfaces, let’s work
on the following assignment: We have 2 components: Grid and List.
Design classes to represent this components. The classes may have the
following methods: doSelect, getSelected and setData.
First we will add all common methods that are applicable to all components to the class ComponentExt
[as]package com.ryaco.ui{
public function setData(data:Array):String{
return "New data: "+data.join(",");
}
} [/as]
In
the next step, we will add a new behavior that can be reused by
multiply classes: an ability to select and get selected row or tab.
Let's define an interface Selectable:
[as]package com.ryaco.ui{
public interface Selectable{
function doSelect(index:Number):String;
function getSelected():String;
}
}[/as]
More then one class can implement this interface
[as]package com.ryaco.ui{
public class Grid extends ComponentsExt implements Selectable{
public function doSelect(_index:Number):String{
//Grid-specific code goes here...
return "Class Grid: Select row number "+_index;
} public function getSelected():String{
//Grid-specific code goes here...
return "Class Grid: return Selected row";
}
}
}
package com.ryaco.ui{
public class List extends ComponentsExt implements Selectable{
public function doSelect(_index:Number):String{
//List-specific code goes here...
return "Class List: Select row number "+_index;
}
public function getSelected():String{
//List-specific code goes here...
return "Class List: return Selected row";
}
}
} [/as]
When
the class Grid declares that it implements interface Selectable, it
“promises” to provide implementation for all methods declared in this
interface - in our case it is two methods doSelect and getSelected. Why
is it so important that the class will “keep the promise” and implement
all interface’s methods? An interface is a description of some
behavior(s). In our case the behavior Selectable means existence of a
methods with the signature
[code]doSelect(_index:Number)
getSelected() [/code]
If any other class knows that Grid implements Selectable, it can safely call any method declared in the Selectable interface
A
class can’t have two independent ancestors, but it can implement
multiple interfaces, it just needs to implement all methods declared in
all interfaces. e.g. class Grid extends ControlExt implements
Selectable, Editable {…}
If you interested only in Selectable
functions, you can cast the object only to those interfaces which you
intend to use, for example:
[as]var seGrid:Grid = new Grid();
var sGrid:Grid = seGrid as Selectable;[/as]
Now
we will write class ComInit, which will use defined classes Grid and
List. It'll create an array with a mix of Grids and Lists. Iterate
through this array and cast it to Selectable interface, and then call
the method doSelect() on each object in this collection
[as]package com.ryaco.ui{
public class ComInit {
public function ComInit (){
var components:Array = new Array();
components.push(new Grid());
components.push(new List());
components.push(new List());
components.push(new Grid());
for(var i: int = 0; i < components.length;i++){
var s: Selectable= components[i] as Selectable;
trace(s.doSelect(1));
}
}
}
}[/as]
Polymorphism
– when you look at our ComInit, it looks like it calls the same method
doSelect() on different types of objects, and it generates different
output for each type. This is an example of polymorphic behavior. It
allows you to avoid using switch or if statements with the type
checking operator is. You don't need to modify the code each time you
add a new selectable component type.
info for java developers:
Comparing the syntax of Java 5 and ActionScript 3.0 links:
AS3 vs Java,
OOP