Some tend to think that Java is a synonym of object orientation done right, some even don’t know other alternatives. But it was always unnatural to me that most of Java classes start their existence with plenty of boilerplate code like this[1]
public class Money { private double amount; public double getAmount() { return this.amount; } public void setAmount(double amount) { this.amount = amount; } }
This is a lot of code to write just to define one single property, a code that is mostly meaningless. But in Java you have to introduce getters and setters from the very beginning, or it will bite you back in the future. It clearly contradicts with the DRY principle and a preference for evolutionary design, which discourages writing code that is useless right now, but may (or may not) be needed in the future. Things get even worse when such code is created automatically by some code generation tool.
In theory your methods should always have some meaningful behaviour, and your should avoid trivial accessors in the public interface. This is a good rule of a thumb, and when it’s broken, this is often a symptom of some wrong design decisions. Though in many practical situations you simply need trivial accessors without any behaviour, for example when mapping relational databases to objects[2].
The whole problem boils down to the fact that in Java you can’t apply the Uniform Access Principle, which states that users of a class shouldn’t care whether a given service is implemented through storage (property) or computation (method). But the syntax for accessing a property and calling a method in Java is completely different, and you can’t start with a simple public property and change it into a method later when it becomes necessary, keeping the public interface intact. So you are told not to use public properties at all and always define trivial accessors just in case
.
I would like to contrast this approach with two dynamic languages, Python and Ruby, each presenting a different point of view on the problem we discuss.
In Ruby — which has been inspired by Smalltalk — properties (instance variables) are always private, and the only way to interact with an object is by sending messages to it. This is similar to a method call, but the meaning is slightly different, and there are certain conventions to make the syntax nicer. You can’t access instance variables outside the class, so the following code
cash = Money.new cash.amount = 10 puts cash.amount
is actually the same as sending messages amount= and amount to the instance, which can be written explicitly as
cash = Money.new cash.amount=(10) puts cash.amount()
This means that Ruby has an uniform syntax for attribute access, but you still have to write message handling methods inside the class. This is where attr_accessor comes in handy (along with its siblings attr_reader and attr_writer), avoiding duplication and making the code more terse. The following piece of code
class Money attr_accessor :amount end
has the same effect as
class Money def amount @amount end def amount=(value) @amount = value end end
When the class evolves and we would like to make accessors more complex (for example implement lazy load or caching) we can replace attr_accessor with real methods, keeping external interface intact.
Python takes a different approach than the message passing metaphor. It publicly exposes all attributes of an instance as slots you can access freely. Inside such slot can be any object (in Pythonic sense of the word), including standard objects (integers, tuples, etc.) and methods. The client simply fetches object from the slot and either invokes it (if it is a callable) or uses its value directly — so the access is not uniform.
To maintain an illusion of uniform access when refactoring a property into a method you can use the property() function, passing new getters and setters as arguments. This means you can start with a class as simple as
class Money(object): def __init__(self): self.amount = 0
Later, when you need some more complex accessors, you can refactor the class with property(), maintaining the same external interface
class Money(object): def _get_amount(self): # Getter code here return self.amount def _set_amount(self, value): # Setter code here self.amount = value amount = property(_get_amount, _set_amount)
As you see the code is not as clear as with Ruby, and there are some other problems with this approach, but it is possible to maintain uniform access in Python.
I believe obeying the Uniform Access Principle is the right way of solving the accessor problem, and both Ruby and Python handle this quite well. If you see trivial getProperty() and setProperty() methods in Python or Ruby code, stay aware. This probably means the code has been written by a programmer who is unable to change his mindset.
—
[1] To convince you this is not a fake example I did a quick search on the Web, finding this piece of code.
[2] Martin Fowler on page 155 of his PEAA book gives example of a class to map a simple person table. He writes it starts with data fields and accessors
and then gives an example of over twenty lines of boilerplate accessor code.
PS. Thanks to Tomek for reviewing the first draft of this article.
Comments 4
In Ruby instance attributes are private, but… there are two methods, namely instance_variable_get and instance_variable_set, which are public and allow “unrestricted” access to instance variables. So I think in Ruby the UAP is fully supported, but you CAN break it.
Posted 09 Feb 2008 at 7:41 pm ¶It was an interesting article for me, as I didn’t know about the UAP before. However, I don’t agree with your assumptions:
> But in Java you have to introduce getters
> and setters from the very beginning, or it
> will bite you back in the future. It
> clearly contradicts with the DRY principle
> and a preference for evolutionary design,
> which discourages writing code that is
> useless right now, but may (or may not)
> be needed in the future.
You say that I have to create getters and setters from the very beginning, while
this is not true. I never do it. The very first time I create getter or setter is when I need to use it from my code.
Described behavior would not only be against the DRY rule, but also could lead to broken encapsulation.
> The whole problem boils down to the fact
> that in Java you can’t apply the Uniform
> Access Principle
Which problem? From my point of view, the problem is not in a language, but in a design.
Posted 09 Feb 2008 at 8:07 pm ¶There is a good article about differences between Java and Python: Python Is Not Java by Phillip J. Eby. It’s surely reading-worth for Java programmers wanting to learn Python.
Posted 09 Feb 2008 at 8:39 pm ¶There is a small bug in the Python version of Money: both getter and setter should use self._amount instead of self.amount.
What are the other problems with this approach?
Posted 30 Mar 2008 at 7:12 pm ¶Post a Comment