Wednesday 11 January 2012

getters and setters are evil

ActionScript has a feature which I have not seen in any other programming language: getters and setters. These are special member functions which when used can be called like direct member access. In some ways this is like operator overloading in C++, except C++ does not allow the '.' operator to be overridden. Having seen how this works in ActionScript it seems like one innovation other languages should not copy.


It's best to start with an example to show how it works. The code to test it is identical to the first accessor test function yesterday:



function Test1():void {
    var iTrial:int, iSum:Number, v:Vec2Da;
    iSum = 0;
    for (iTrial = 0; iTrial < kNumTrials; iTrial++) {
        v = aDataA[iTrial];
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
    }
}



But unlike yesterday the second function is identical. Except for changing the type of the data it looks the same (as before the whole example is available on wonderfl). The difference is in the definitions of the classes.


The first class, Vec2Da, is similar to the one used yesterday, except the accessors have been removed as they are not needed.



class Vec2Da {
 public var iX:int;
 public var iY:int;

 public function Vec2Da(x:int, y:int) {iX = x; iY = y;}
}

The second class Vec2Db has two private members, and uses setters and getters to access them. This to external code makes it look like iX and iY are public members. Without inspecting the class it's not possible to tell from the code that setters and getters are used.

class Vec2Db {
 private var _X:int;
 private var _Y:int;

 public function Vec2Db(x:int, y:int) {_X = x; _Y = y;}
 public function get iX():int {return _X;}
 public function get iY():int {return _Y;}
 public function set iX(x:int):void {_X = x;}
 public function set iY(y:int):void {_Y = y;}
}

Unless that is you look at performance. Using getters and setters is slow, and not just by a little; as yesterday accessing members directly is over four times faster in the test app. getters and setters are just as slow as accessors.

They are worse than accessors for two reasons. First they are impossible to detect in code calling them. It's easy to write much slower code without realising it because the code looks identical to code with reasonable performance. This is especially a problem with libraries where you don't have access to the code to see how it is implemented, though it could happen with any code, especially a large program.

Second they are worse because of what they imply about the language. When something novel is added to a programming language it usually offers some benefit such as performance or a new feature. These do not add any new capabilities to the language, nor any new syntax, so logically they should provide a faster way to do things.

But clearly they do not: they are just a slower way of accessing members / a way of hiding the performance penalty of accessors. I never use them and I would recommend anyone to do the same. If you have to use regular accessors. At least then if you or someone else looks at the code six months later it will be clear from the code that a (slow) function is being called and the code will be much easier to optimise.

2 comments:

  1. I'm no Actionscript expert, but I think this will apply though. The main fact is that accessors (in any language) will potentially bring evilness in your program. By using accessors, you're exposing internal state to the world. That's not the way objects were intended to be used.

    A much better idea is to follow the "Tell, Don't Ask" principle: instead of asking for some state and then execute some piece of code with the data retrieved, move that behavior to the object carrying the data and command it to do the task itself.

    Cheers.

    ReplyDelete
    Replies
    1. The problem with ActionScript (or more accurately with AVM2 the runtime), is all methods are slow; not just accessors but methods such as you describe. For objects that are manipulated frequently it's therefore more efficient to access their members directly, not call functions of any kind. It means unlearning a lot of good programming practices, at least if you want your game to run quickly and smoothly.

      Delete