Monday, May 12, 2008

Properties: A Matter of Privacy

I'm going to take a weird tangent today and talk about something non-graphical. Odd, I know, and perhaps a bit disturbing. Like Britney Spears writing a book about effective parenting. Or a politicians working on legislation instead of their re-election campaigns. Nevertheless, I feel compelled to discuss this language feature thatI encountered into it in my recent transition to writing ActionScript3 code.

For someone coming from the Java world, the way that properties work in ActionScript3 requires a mental shift. AS3 properties are pretty cool, but they encourage a different pattern of programming that's new to me, so I thought I'd spend a few words on it for anyone that is also new to properties.

The Ways of Java

In Java, the general rule is to make the properties (or variables) of a class private, preserving encapsulation (if I were a better Java acolyte, I'd quote you the chapter and verse where Effective Java discusses this pattern). If you need to expose the properties through public or protected API, you should create a setter/getter for the property with the appropriate access. For example, instead of this:

    public class Blah {
        public Freen freen;
    }

you would typically do this:

    public class Blah {
        private int freen;
 
        public void setFreen(int newVal) {
            freen = newVal;
        }
        public int getFreen() {
            return freen;
        }
    }

Callers of the API would then call setFreen() and getFreen() instead of referring directly to the instance variable freen.

One good reason for taking this approach in Java, even for simple situations where the getter/setter do nothing besides getting/setting the value, is to make the API future-proof. That is, suppose you decided to track the number of times that freen is set. In the first example above, there is no obvious way to do this other than adding new API that callers must now use. But in the second example, all we have to do is add the necessary logic to our setter function, and we're set:

    public class Blah {
        private int freen;
        private int numSets;
 
        public void setFreen(int newVal) {
            freen = newVal;
            numSets++;
        }
        public int getFreen() {
            return freen;
        }
    }

Notice, in particular, that there is no change to the public API for Blah to add the functionality of incrementing numSets; we buried that new functionality inside the existing API that your users are already calling.

Taking Action[Script3]

Now consider the case of ActionScript3. You could take the approach above and implement setFreen() and getFreen() exactly as we've done in the Java code, modulo differences in how types and functions are declared:

    public class Blah {
        private var freen:int;
 
        public function setFreen(newVal:int):void {
            freen = newVal;
        }
        public function getFreen():int {
            return freen;
        }
    }

But this would be silly because you'd be missing out on all of the advantages of the built-in Property capabilities of the language. It would be like going to France and continuing to eat McDonalds every day because that's what you eat at home. Sure, it does the job, but there are probably better and more interesting ways of accomplishing that task in the new place. Besides, their ketchup tastes weird.

In ActionScript3, there are two special qualifiers on functions, set and get, that make dealing with properties much more flexible. When you declare a function with set as a qualifier, callers call that function by simply specifying the name of it as they would any instance variable. For example, given this class defintion:

    public class Frong {
        private var _glar:int;
 
        public function set glar(newVal:int):void {
            _glar = newVal;
        }
        public function get glar():int {
            return _glar;
        }
    }

callers would set and get the value of _glar like this:

    var frong:Frong = new Frong();
    frong.glar = 5;
    var someInt:int = frong.glar;

So how does this factor into our discussion of private/public API? Because we can migrate our class over time to use public properties and public setters/getters as appropriate, without affecting the API. Let's take the original example, where we simply wanted a public variable freen:

    public class Blah {
        public var freen:int;
    }

Now suppose, as before, we want to add a mechanism to track the number of times freen has been set by the caller. We can change the above code to the following:

    public class Blah {
        private var _freen:int;
        private var numSets:int;
 
        public function set freen(newVal:int):void {
            _freen = newVal;
            ++numSets;

        }
        public function get freen():int {
            return _freen;
        }
    }

This is almost like our earlier ActionScript example where we defined setFreen() and getFreen() functions, but here the function names are just the name of our original variable, freen. But the really important thing is that our public API did not change. We were able to go from a public variable, freen, to a public setter/getter pair for that variable with no perturbation in how the callers of our API interact with that API. With either approach above, callers would do the following to get and set freen:

    var blah:Blah = new Blah();
    blah.freen = 5;
    var someInt:int = blah.freen;

Why It Matters

The whole reason for this post, and perhaps for this capability in the language, is that ActionScript3 developers can just think in terms of properties in their API, instead of in setters and getters for those properties. Developers can, and probably should, use the more terse declaration of public properties, versus the Java approach of public setters/getters, knowing that if more functionality is needed at set/get time, it can be done without changing the API that callers use.

p.s.

As Josh pointed out in the comments after I first posted this entry, it is not possible to override a variable with a function in a subclass. So if you are developing an API and expect or want your developers to be able to modify the behavior of a property via set/get, then you should declare your properties via the set/get functions.

4 comments:

Josh Tynjala said...

Good information. Thanks for sharing. Some food for thought: When building an API that will be used by other developers, you should always use get/set functions rather than public variables because they may want to override the behavior of a getter or setter and you can't override a variable.

guibuilder said...

this was one of the few things i found really weird while working with actionscript , thanks for pointing it out ... Also how did the FRC session at JavaOne go on... Please update !

Somatik said...

Thanks for the info, I'm making the same move from swing to flex :-)

Chet Haase said...

Josh: Good point - I'll add something to the article as that's an important case to consider.

Guibuilder: Our Filthy Rich session went well - new whizzy effects, big crowd, good time.