Thursday, January 06, 2005

Accessors: yes or no?

There is a referenced article against accessors in Blaine's blog. In that article, you can find the following assertion:

A fundamental precept of OO systems is that an object should not expose any of its implementation details. This way, you can change the implementation without changing the code that uses the object. It follows then that in OO systems you should avoid getter and setter functions since they mostly provide access to implementation details.

The conclusion does not follow. Messages provide behavior, and if you complain that the accessors are giving you implementation details it's because you are being too smart.

In other words, if you look at an object that understands a set of messages and, violating encapsulation, you look at the implementation details and find that some of those messages are accessors, then you complain because the object is violating encapsulation. But wait a minute: who was the smarty pants developer that violated encapsulation in the first place?!

Sigh... another instance of curiosity killing the sender.

When you send a message, you should only care about the behavior. Then why is it that a sender of a message needs to care about whether a message is an accessor or not? Senders should stay at whiskers' distance from the receivers.

If you, following proper programming practices, judiciously send a message for a good reason, whether it's an accessor or not is irrelevant. If you need to send the message, then chances are it makes sense, therefore any implementation should implement it. How this is done is the implementor's business.

Blaine's main conclusion is that whatever you choose, you should be consistent. I would like to add that one of the few inconsistencies left in Smalltalk is that assignment is not a message, thus violating the tenet "everything is done by sending messages".

In my opinion, sticking the instance variable assignment in an accessor helps containing the inconsistency in a rather small spot. This "almost consistency" then helps out in the following ways.

1. Using accessors provides a uniform referencing mechanism that can tell who is using the instance variable by just asking for senders. If you don't use accessors and want to find who sets and reads the instance variable, you are forced to ask for references to the instance variable first, followed by reading all the methods to make sure they don't propagate the instance variable access responsibility somewhere else, making sure that you are not reading superclass implementations that are refined in the subclass you are interested in, oops then you find that the messages referencing the instance variable call each other so now to understand you need to walk the graph of the code making sure you keep in mind at which point things are read/written...

... and by this time you need to remember more than 7 +- 2 things, and just sending senders was more efficient, less time consuming and less prone to code reading mistakes to begin with.

2. Code without accessors is hard to change, too. Do you want to add or remove lazy initialization? What about clustering some instance variables in a new object? What if, oh no... you need to move some methods to another object? Aaargh! Now something that should be trivial becomes a half-hour refactoring task! This is just another small accomplishment having its value artificially increased.

3. Instance variables without accessors are object-wide globals. We already half-despise class variables and pool dictionaries. Why do instance variables without accessors get any mercy in our refactoring quest?

Too bad temporary names can't have accessors too :(. But on the other hand, always using accessors for instance variables implies that all assignments you see in something other than an accessor must be to a temporary name - "almost consistent".

No comments: