Monday, April 20, 2009

Properties Survey

The object-oriented programming tenet of encapsulation can lead to some ugly looking code. Objects should not expose their internal state, but instead provide access through methods. The related ugliness is most commonly seen in the Java programming language:

public class User{
private String name;

public String getName(){
return this.name;
}

public void setName(String name){
this.name = name;
}

public static void main(String[] args){
User user = new User();
user.setName("Michael");
System.out.println(user.getName());
}
}

I suppose ugly is a subjective term. Perhaps verbose is the better term. Many other languages offer syntax that is simplified, but without losing encapsulation. For example, here is similar code in Ruby:

class User
attr_accessor :name
def name=(name)
puts "setter called"
@name = name
end
end
user = User.new
user.name = "Michael" #setter called
puts user.name #Michael

In the above code, the getter and setter are both generated by the attr_accessor method. The syntax makes it seem like the internal state is exposed directly, but there really are methods being generated and invoked. To demonstrate this, I chose to override the setter and added in some extra code to let you know it had been called. Ruby is a very powerful language, and its meta-programming (i.e. code that writes more code for you, like the attr_accessor method above) are renowned. However, it is hardly the only language that does properties like this. Good 'ol Python can do it too:

class User(object):
def getname(self):
return self.__name
def setname(self, name):
print "setter called"
self.__name = name
name = property(getname, setname)

user = User()
user.name = "Michael" #setter called
print user.name #Michael

Not quite as succinct or as elegant as Ruby, but it works. Slick properties were added to Objective-C not too long ago as well:

@interface User:NSObject{
NSString* name;
}
@property (copy) NSString* name;
@end

@implementation User

@synthesize name;

- (void) setName:(NSString *)name{
[self.name autorelease];
self.name = [name copy];
}
@end

The header file makes this more verbose, as well as the memory management. Objective-C has a garbage collector, but only for its desktop profile. You have to manually manage the memory if you are writing Objective-C for an iPhone application.

ActionScript 3.0, which is based on the now defunct EcmaScript 4.0 specification also has properties. The syntax is a little verbose


class Person{
private var _name:String;
public function get name(){
return _name;
}
public function set name(name:String){
trace("Setter called");
_name = name;
}
}

Note that you have to define both the get and set functions. This gives you the "dot syntax", i.e. you can do something like myPerson.name = "Michael". Scala is similar:

class Person{
private var _name:String = null

def name:String = _name
def name_=(name:String){
println("setter called")
_name = name
}
}

object App{
def main(args:Array[String]){
val person = new Person
person.name = "Michael"
println(person.name)
}
}

Scala can generate the getter/setter for you, but you cannot override them. So if all I wanted was the default behavior, nothing extra, I coudl just do

class Person{
var name:String=null
}

object App{
def main(args:Array[String]){
val person = new Person
person.name = "Michael"
println(person.name)
}
}

There is no way to override the methods that Scala generates for you. Even if you subclass Person, you cannot alter the generated getter/setter methods.

No comments: