Forget About Writing Accessors

Part of the CppHeresy.

The Java semi-heretics have er, done some of this. Kind of. JythonLanguage allows you not to care whether there's a public getX() or just a public X. Either way object.X means the same. So why have an accessor they've been wondering? And sometimes not even having one. Maybe.


In Clipper (ClipperLanguage?) we added something called ACCESS and ASSIGN methods to our class definition syntax. Instance variables could only be private but could be accessed and assigned as fields but written like methods. The usual stigma of using fields was removed as you could change its access and assignment code without impact client code. Something like...

 class Rectangle super Object

ivar top ivar left

[...]

public access top() return self:first

public assign top( nTop ) if ( nTop < 0 ) throw XBoundaryError:New( "top must be greater than zero" ) endif

return self:top = nTop

public method getOrigin() return Point:New( self:top, self:left )

[...]

end class

It's been a while, but it went something like this. It had a lot of neat implications and some not so neat but the basic idea was that reflective components like JavaBeans could be supported with language support instead of naming conventions as Java requires. I'm not familiar with JPython but is this the sort of thing you are talking about? -- RobertDiFalco


Suppose you are writing an accounting program that is required to supply a method called #currentBalance, which answers the current balance of the account.

Is there an instance variable that contains this information, or is the current balance recomputed each time it is needed?

This is a design decision that is influenced by a host of factors -- and often changes after the system is "live" and in use.

An important reason for accessor functions, in an interface, is to isolate the consumer of module from such design decisions. As a consumer, I just want to know the balance. I don't want to have to, for example, change my code because some development team in New Delhi decided to turn a function into an instance variable.

I might add that the Java convention of prepending "get" to the name of the instance variable manages, like many Java compromises, to deliver the worst of all worlds -- it preserves and surfaces the distinction, while adding the overhead of a method call. This is particularly ironic in a language like Java, because Java argument overloading solves the is-it-a-reader or is-it-a-writer problem.

Here's the convention (assuming that only read access is publicly supported):

   //If instance variable
   public Money getCurrentBalance();
   private void setCurrentBalance(Money aBalance);

//If computed public Money currentBalance();

Here's a much better (IMHO) approach:

   public Money currentBalance();
   private void currentBalance(Money aBalance);

In the second "better" approach, consumers of the module don't care whether there is a member variable or not -- they merely ask for the currentBalance when they want it. Yes, it's true that "getCurrentBalance" could be used even with no member variable, but then the "convention" has merely added extra characters to the function name.

-- TomStambaugh

Well put. In some languages, notably Eiffel and C#, the client doesn't need to make a syntactic distinction between accessors and member variables. After a few years of get/set in Java, I'm inclined to appreciate the simplicity this seems to allow, but I'd prefer a convention other than get/set. --StevenNewton

While this is true for Eiffel (and Ruby, and Python, and Smalltalk) this is completely wrong for C#. A C# property is completely distinct from a member variable: it can be used in interfaces (while member variables can't), the Microsoft C# style guide requires that it use a method-like casing (PascalCase while member variables are camelCased), and you can't completely transparently swap back and forth between properties and member variables.

Oh, it's not quite as clear-cut as all that in C#. The capitalization conventions described in the MSDN call for public members (of all sorts!) to use PascalCase. They mention that this rule doesn't apply to fields because fields shouldn't be public, but if you look at the "Member Design Guidelines" articles, there are a number of cases in which it is considered OK to expose public fields. There are examples of such cases in the runtime, and these public fields use PascalCase. (An exception being Math.PI, which really ought to be Math.Pi, don't you think?) You can often switch between properties and fields without affecting client code -- modulo some issues with versioning and security, which in many situations are not a concern. Etc. etc. In those situations, the recommendation against public fields needn't apply. Completely wrong? No, just some caveats. -- DanMuller


Couldn't agree with you more about the "get" convention, Tom. But doesn't how much isolation a consumer needs from the design vary according to the circumstances? If the team developing the module is on the other side of the world then I definitely don't want to have to change my code. If the team developing the module is my team, then they can just change all of our code themselves.

I keep hearing a little voice in my head saying "YouArentGonnaNeedIt" when I write accessors. I haven't listened to it yet, but I might soon.

--ChristianTaubman

I don't think it's an all-or-nothing proposition. You don't want to write a data class with no behaviour and a get/set for each member. But would you grant direct access to member "pointer_or_handle_to_dangerous_device_that_may_or_may_not_be_initialized"? --AndrewQueisser

If I find myself writing a mindless getPointerOrHandleToDangerousDeviceThatMayOrMayNotBeInitialized(), then yes. Of course, hopefully I won't...

Exactly! If this page hadn't come from CppHeresy (which, incidentally, made an entirely different point) we should rename the page to ForgetAboutWritingMindlessAccessors?. --AQ


I keep hearing a little voice in my head saying "YouArentGonnaNeedIt" when I write accessors. I haven't listened to it yet, but I might soon.

I agree that when code is typed by hand, method by method, YouArentGonnaNeedIt might apply. On the other hand, when the accessors come for free -- either because the IDE puts them there, or because they come from a pattern/code generator that puts them there -- then the effort to decide "should I or shouldn't I" (or worse, to selectively remove them) in my view becomes an important factor.

I end up using them, habitually, in Smalltalk and Java because I've found that it takes me more time to find and fix the bugs that happen when I leave them out. As nearly as I can tell, they introduce no measurable runtime overhead.

A significant motivator for YouArentGonnaNeedIt is the risk that results from increased complexity. In the case of accessors, the accessor patterns that I use are so well-exercised and unchanging that there is virtually no risk; the code is bug-free because the pattern is well-exercised.

I think this is analogous to framing a wall with studs. It is certainly true that putting a stud every 16 inches is overkill -- the wall doesn't "need" nearly that much lumber. On the other hand, it's easy to remember "every 16 inches", and it doesn't do any harm (beyond the costs of the "extra" lumber). While there are certainly very unusual times when an engineer might specify something different (a lightweight truss or something similar), by and large a rule-of-thumb is easier and leads fewer costly mistakes.

For me, the gain in consistency that I get from knowing that they will always be present outweighs the possible offsetting increase in complexity.

-- TomStambaugh

Yes, I suppose that's why I still write accessors too. It's nice to be able to say you only ever send messages to an object. Perhaps in this case, as in others, a little duplication is a small price to pay for clarity. --ChristianTaubman


[Parenthetically, how nice that RandomPages turns up thought-provoking little gems like this.]

In the case of public classes surely you more or less have to use accessors, even for immutable fields, unless you are sure you you will never want to reference them through interfaces later. This can really only be the case for 'pure data' classes without methods that correspond more or less to the C struct.

On the other hand, it can be handy to mark immutable initialisers within packages (and sometimes in non-API public classes) by making them directly accessible but final. This expresses the immutability very clearly, and often forces you to think harder before creating multiple constructors (the compiler complains when you either fail to assign or try to reassign the blank finals). - DavidWright


Actually, PythonLanguage even takes care of the "what if I need them later" problem, as properties can take the place of class members after the fact and without affecting client code:

 class Counter:
     def __init__(self, count):
         self.count = count
     def bump(self):
         self.count += 1

def use_counter(): c = Counter(0) c.count = 5 # Now c.count = 5 c.bump() # Now c.count = 6

# But now we need to provide an optional maximum, and wrap access to count: class Counter: def __init__(self, count, max_count=None): self._count = count self.max_count = max_count def bump(self): # No change here! self.count += 1 def _get_count(self): return self._count def _set_count(self, new_count): self._count += new_count if self.max_count is not None and self._count >= self.max_count: self._count = 0 count = property(_get_count, _set_count)

# And now use_counter still works, without changes.


EditText of this page (last edited February 27, 2007) or FindPage with title or text search