NOTE: This document may be circulated or quoted from freely, as long as the copyright credit is included.
What should we think of a Complex number class
(C++ or Java)1 that
provides the following methods:
double getRealPart()
double getImagPart()
Complex conjugate()
double abs()
Complex sqrt()
Is there something inconsistent about it?
Suprigingly, many object-oriented programmers not only see nothing wrong there, but
endorse it as their preferred way of designing accessor methods. Why,
we ask them, are two of the function names prefixed by
get while the other three aren't?
"'Easy!" they reply. "The first two are bona-fide accessors that retrieve private member data items, while the others perform a computation."
Wrong! (in several ways)
One of the essential characteistics of object-oriented programming is information hiding. The user program isn't supposed to know how an object is internally represented. The private member data items are none of the user's business.
That lets us change the internal representation of a class of objects without requiring corresponding changes to programs that use the class. For example, suppose we add accessors for the polar representation:
double rho() — or should it be
getRho() ?
double theta()— or should it be
getTheta() ?
Then for the sake of performance tuning we might decide to change the internal representation of a number in the complex plane from Cartesian (real and imaginary parts) to polar (distance from origin and angle with the real axis). Which are now the pure accessor functions that just return a private data item? No user knows and no user should care. Except for execution speed, no user program should be at all affected by such a change.
That's an essential and generally considered desirable feature of the object paradigm.
"All right," our programmer concedes, "but what about write accessors (set methods)? Isn't it good to be consistent in naming?"
No, not if making getReal consistent with
setReal forces it to be inconsistent with
abs, for example.
First of all, many of the write accessors (set
methods), perhaps a majority, that we find in object-oriented classes aren't needed at all.
Some younger programmers have picked up the misguided notion that
coding a
get and set
pair for almost every member data item is some sort of "canonical form" that object-oriented
programs are expected to follow.
In Mathematics function names are typically nouns that stand for the returned value.
That tradition was carried over
into early Computer Science (a branch of Mathematics). Programming languages that support
both function notation and a call statement
managed to preserve the useful distinction.
Almost anything you can do with a write acessror you should be able to do with a constructor2, creating a new object with the value you want.
The bottom of the barrelBy far the worst abuse of the accessor concept lies in providing this blatant undermining
of the object paradigm: Need we discuss the outrageous corresponding
|
A horrible standard exampleAmateurish design of an elementary numeric class doesn't get much worse than Java's standard
|
More than other object-oriented languages Java tempts programmers to support
getValue() as a way of circumventing Java's
lack of operator overloading. Apparently many Java programmers
using a Money class prefer to code:
totalCost = new Money(unitCost.getValue() * quantityOrdered);
rather than
totalCost = unitCost.mpy(quantityOrdered);
even though the former violates encapsulation. A competent C++ or C# programmer, of course, would simply code:
totalCost = unitCost * quantityOrdered;
Designers of object oriented classes, then, should avoid:
get.
Just use good old-fashioned function names.
set functions at all except in
unusual situations. Unless there's a compelling need to modify one private member
data item without reinitializing the whole object, don't do it.
Return to IDI Home Page
Return to Technical articles
Last modified May 12, 2012