Make your own free website on Tripod.com

The getter/setter property
by ericlin


download zip

What are getter/setter property ? Why and when do we need getter/setter property/functions ?

Getter/setter properties can be attached to any object. Usually they are attached to a Class. So, lets take Class as examples.

The "Circle class" as an example

Suppose we want to create a "Circle" class.

Circle.radius

The first property we plan to give is the "radius". It is not a getter/setter property. Its behavior is nearly the same as a usual variable.

When we try to store or change a vairable value, we "set" a value. When we want to know what the variable value, we say "get" a value. Usually, the syntax is very simple. For example, we want to change the radius of a circle "circle1" to be 5 , we say "circle1.radius=5;". If we want to know what the radius of that circle is, we say "trace(circle1.radius);" This is how we "set" and "get" the value of the radius.

Circle.area

Lets check a more complex condition. Now we want to add a property "area".


getter function

How do we "get" the area of the circle ?  Most of the time, we get the area through the radius rather than picking a value stored in a variable "area". So, at present, we could not just do "trace(circle1.area);" We need to do "calculation" to get the value of the area. So, we write

trace(circle1.radius*circle1.radius*Math.PI);

We may well write a function "getArea()" to do the calculation:

getArea=function(){
    return (radius*radius*Math.PI);
}

To get the area of the circle1, we say "trace(circle1.getArea());"

Now, see what will happen if we make it a getter method.

In AS1, the syntax is:

Circle.addProperty("area", getArea, null);

I will discuss about the setter function later, so we just make setter function as null.

In AS2, the syntax is:

function get area(){
    return (radius*radius*Math.PI);
}

Please note the space between "get" and the property "area".

This syntax means that function is a getter function for a property "area". If we want to "get" the value of that property, the getter function is triggered instead of picking the "value" stored in "area". Now, we can simply say "trace(circle1.area);" to get the area of the circle instance. Literally, it seems we are peeking the value stored in a property "area". In fact, it is not. What Flash does is to execute the getter function and throw the result to us. If radius is 1, when we say "trace(circle1.area);" , it outputs "3.14159";

Please note that, the job "trace(circle1.area);" is much heavier than "trace(circle1.radius);". The job is finished with complex calculation.


setter function

Lets see the case of "setter" function.

We want to enlarge the circle to make the area to 100; Our goal is to "enlarge" the circle, not just making a stored variable value to 100; In other word, we are going to change the radius properly so that the area will be 100. So, the script should be 

circle1.radius=Math.sqrt(100/Math.PI); 

At present, a script like "circle1.area=100;" makes nonsence for the effect of "enlargement".

We may write a function:

setArea=function(area){
    radius=Math.sqrt(area/Math.PI);
}

To enlarge the circle, we say "circle1.setArea(100);". That will enlarge the circle by setting larger radius.

Now we make it a setter function;

In AS1, the syntax is:

Circle.addProperty("area",getArea, setArea);

In AS2, the syntax is:

function set area(area){
    radius=Math.sqrt(area/Math.PI);
}

Please note the space between "set" and the property name "area".

This syntax means that, we assign a setter function to the propery "area". When we try to modify the value of that property, the setter function will be triggered to handle this job. Now we can use the syntax "circle1.area=100;" to enlarge the circle.. You see, we can use "=" to do the assignment now. Literally, the syntax seems to assign a value to a variable "area". In fact, it is not so simple. Flash just calls this setter function when we do assignment.

Please note that, the job "circle1.area=100;" is much much heavier than "circle1.radius=5;". The job is done through complex script. 

So, we add a property "area". When we access this property, the getter function is called and a result is returned. When we try to assign a value to this property, the setter function is called with the value in the argument. The result is setting the radius rather than setting the "area" variable.

You may have smell something similar to Object.watch. By that, when we access a property, a callback function is triggered and the old value is modified to a new value. However, be it similar in some point, it is different from getter/setter mechanism.

The syntax to get and set the area, ("trace(circle1.area);" and "circle1.area=100;") are the same as usual variables. From syntax, we can not tell whether it is a getter/setter property or an ordinary variable. Anyway, the syntax is much handy than scripting a function call.


Put the "convenience" and "handy" aside, why in the hell do we need to make a property getter/setter ?

There are reasons. 

wraping OOP in Class

Class may contain many properties. The values of which are dependent on and related with each other. For example, in our Circle class, any change of the radius should change the value of the area. If we change the area, the radius should not remain unchanged. If we have just one variable to store "radius" and the other variable to store "area", then it is difficult to implement the "dependence". So, we make the property "radius" as a usual variable as core property but make "area' a property of getter/setter type. Then the "dependence" is maintained.

Many properties of MovieClip are getter/setter type. Setting the _width will change the _xscale. Changes of _yscale will affect the _height.

You may do a practice. Try to add a property "diameter" to the Circle class. When I say "circle1.area*=4;" and trace(circle1.diameter);" should output a value of double the original diameter. When I say "circle1.diameter*=2;" and 'trace(circle1.area);" should output a value quatriple the old area value.

OK, go ahead. I dont think you can do it without getter/setter functions.


Difference between ordinary property and getter/setter property

I know it is function called beneath the table. Since the syntax is the same for "radius" and "area", is there any significant difference in other aspects ?

Object.watch

First, Object.watch can not be applied to getter/setter properties. We can watch the "radius" property, and when we want to assign a value to "radius", a callback function is triggered, and a new value is assigned to "radius". In the case of "area", any assignment triggers the setter function. We can not apply a watch to this property.

Many properties of MovieClip, such as _x,_y,_xscale,_yscale,..... are all getter/setter function. We can not apply Object.watch to these properties.


get != set

Second, the getter function might not return the exact value we pass through setter function. When we "set" a value to that properties, a setter function is called. The setter function might contains scripts with complex calculation. It is not so simple as storing a value to a memory.

When we say "mc._x=98.32745", Flash calls an intrisic getter function, and update the screen. When we do "trace(mc._x) ;" The output is "98.3", not "98.32745"; When we script "_rotation=270;" and "trace(_rotation);" we get "-90" rather than "270"; Such modification wont happen in ordinary variable. Usually, we get what we set.

Of course, it is not always so bad. In our example, when we say " area=100;", the result is to set radius=5.64189583547756; Then we say "trace(area);", the area is calculated from the value of radius. Voila, we get the value of "100". It is exactly the same as we "set". So, it is not always so bad.


Return value from setter function ?

b = a = 3 ;

When we say "b=a=3;", Flash asigns 3 to the variable "a" by "setVariable", and remain the value 3 in the stack. Then it performs b=3, assigning whatever remains on the stack to the variable b by "setVariable", and the value remains in the stack. Please note that, the syntax has nothing to do with (a=b); This syntax does not assign what in viariable a to variable b; In other word, "b=a=3;" is interpretated as: 

a=3;
b=3;

not 

a=3;
b=a;

The script {a=_rotation=270;} will result in _rotation=-90, and a=270;

The script {a=_rotation="foo";} will result in a="foo", but no effect to the _rotation;

In the case of setter property, the process is the same with the command being either "setVariable" or "setMember". The original value is still the original value without modifications.

Setter function is always defined as a VOID function. It does not return any value out.

What if the setter function does return a value out ? Will it affect the result of setVariable or setMember ?

NO. At least NO for the present.

In AS2, all setter function is defined as VOID. However, the compiler will modify the script and make it return a value by a getter call. When we say "circle1.area=100;", the getter function is called at the end. However, the the return value does not return to the stack. The behavior of "setVariable" or "setMember" does not take over the return value.

Why does Macromedia implement a getter function at the end of setter function ? I dont know.


debug movie and getter function

Is there any difference between debug movie and test movie ? Yes, the calls of getter function to initialize all properties.

In the menu bar, Ctl-Enter will trigger "test movie" while Ctl-Shift-Enter" will triger "debug movie". When an instance of Class is created, its __proto__ chain is first setup, then constructor is executed. This is what test movie goes normally.

In case of debug movie, when an instance is created, the debugger needs to fetch the initial value of all the properties before the constructor is executed. So, we see that Flash scans all the properties and every getter function is called before constructor.

Such difference (initialization call of getter function) is "usually" innocent and harmless. Usually we dont implement actions to modify anything in the getter functions. Anyway, this is an information we should know when we "debug movie";


The protection effect through getter/setter functions

Since any access to the getter/setter property is redirected to a function, we can control the accessment.

What will happen if a property has getter function but no setter function ?

That makes a property readOnly..

There are many "readonly" properties in UIComponent.

readOnly properties in UIComponent

Try the x and y property of any UIComponent, such as ComboBox; We can get the x position by trace(myCombo.x) but we can not move the x position by myCombo.x=200; If we trace(myCombo.x), it is still the old value. The script "myCombo.x=200;" does nothing. It will not set x to be 200;

Also, the x property is defined as getter/setter property, we can not use it as custom property any more.


Inheritance of the getter/setter property

If we create a SuperCircle extends Circle class, will the getter/setter property be inherited ?

Yes, the property gets inherited.

Can we over-ride the getter/setter property in SuperCircle ?

Yes, by using getter/setter properties.  The codes below does not really over-ride the property "area". It just initiate the "area" through getter/setter functions of the ancestor.  

class SuperCircle extends Circle {
    public var area:Number=50;
}

By this script, we might thought the property "area" is a property of variable type. In fact , it is still a property of getter/setter functions through inheritance.


Delete the getter/setter property

We can create a getter/setter property to an object. ( I am not talking about Class).

function getX(){return this.$x;}
function setX(xx){this.$x=xx;}
myObject.addProperty("X", getX, setX);

Now the property X is a getter/setter property. 

"X=null;"  will result in this.$x=null;

"delete X" will clear the property X and clear the association with getter/setter functions. The value of $x remains.

Of-course, if the property comes from inheritance, "delete" has no effect.


At the end of this discussion, I like to give two examples to demonstrate the getter/setter properties. These examples are very helpful to understand to power of getter/setter functions.

Tooltips controller

To enable a movieClip to show tooltip, we modify the onRollOver handler. We call our "showTooltip" functions together with onRollOver scripts which might contains animation and manipulation of properties.  We can create a "showTooltip" function and a "clearTooltip" function. Just remember to call them in the onRollOver and onRollOut event handler. Remember this, each time we want to change the content of onRollOver and onRollOut. 

This approach needs too many "Becareful, Don't forget to include this and that !".

Here is a project is to create a TooltipController. I want to create a method  

TooltipController.initiate(targetClip:MovieClip, tooltipText:String);

which will make targetClip popup a tooltip showing the tooltipText;

In summary, we want to plug some script to the onRollOver and onRollOut event handlers.

targetClip.onRollOver=function(){
    TooltipController.showTooltip();
    Users_onRollOver();
}

The first step is to preserve the old onRollOver function. This is simple. We backup the original onRollOver handler and make a new onRollOver function to show tooltip and then call the old onRollOver function in the backup.

The second step is to preserve the flexiablity of onRollOver function. This is annoying. Some where, some time, the user might want to re-assign a new job to the onRollOver handler. If he scripts "target.onRollOver=newJob", then the showTooltip function is lost. To preserve the function, he should only manipulate the old onRollOver function in the backup. He should script: "Users_onRollOver=newJob;" instead of "onRollOver=newJob;".

How to solve the second step ? Ok, this is just a matter of "re-direct". It can be done by getter/setter functions. When user try to "set" onRollOver function, we redirect the setting to Users_onRollOver handler.

function set onRollOver(fun){
    Users_onRollOver=fun;
}

OK, you should try to write the getter function by yourself.

For convenience, I might use AS1 or AS2.

The fla is included in the zip file


Hack the onLoad

There have been a problem about onLoad handler when we load external SWF to a movieClip. When external SWF is loaded to the loader clip, all custom data in the loader clip are cleared including the onLoad handler. When external SWF is on load, there is no onLoad handler to handle this onLoad event.

There are many work-arounds. Here is the one I like. It re-direct the "onLoad" property.

Instead of storing the onLoad handler in the loader clip, we store our onLoad handler in other place. Then we figure a way so that when external SWF is loaded, it finds onLoad handler in other place rather than searching in the loader clip itself.  

When SWF is loaded, Flash try to find the onLoad handler, it is a "get". While we assign a function to be the onLoad handler, it is a "set"; So, the solution is to make the MovieClip.onLoad a getter/setter property, with getter function returning our outside handler and a setter function to set our outside handler.

function getOnLoad(){
    return _global[this._target+"_onLoad"];
}

function setOnLoad(onLoadHandler){
    _global[this._target+"_onLoad"]=onLoadHandler;
}

MovieClip.prototype.addProperty("onLoad", getOnLoad, setOnLoad);

Thus, our onLoad handler is in the _global site. When we assign an onLoad handler to a loaderClip by "_root.loader.onLoad=job;" , the result is " _global["/:loader_onLoad"]=job;" It will not be cleared by loading external SWF.  When onLoad event occurrs, Flash want to get the onLoad handler of "_root.loader", and our getter function return it a value "_global["/:loader_onLoad];" , that is "job". It pick the reference of "job" and do "job();"

Please note that, onLoad handler can not be fully applied to loadMovieNum. If there is not any SWF in the _level1, the _level1 is null. We can not script "_level1.onLoad=someThing;", because _level1 is null;


ericlin@ms1.hinet.net