Pet programming peeves . . .

You can get some of the data all of the time, . .
© Conrad Weisert, Information Disciplines, Inc., Chicago
11 May 2012

NOTE: This document may be circulated or quoted from freely, as long as the copyright credit is included.


Question

What should we think of a Complex number class (C++ or Java)1 that provides the following methods:

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)

Fundamentals of the object paradigm

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:

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.

Symmetry of read and write accessors:

"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 accessror you should be able to do with a constructor2, creating a new object with the value you want.

The bottom of the barrel

By far the worst abuse of the accessor concept lies in providing this blatant undermining of the object paradigm:   public double getValue() When we see getValue in a class definition, we should instantly reject any thought of using that class. If it's in a student's exercise, I ask the student what it could legitimately be used for. What on earth did the programmer think the difference is between the value of an object and the object itself?

Need we discuss the outrageous corresponding void setValue(T v) function?

A horrible standard example

Amateurish design of an elementary numeric class doesn't get much worse than Java's standard Date and Calendar classes. In addition to all the problems we noted in the original version, which haven't been corrected, the torrent of get and set functions almost has the flavor of a parody of bad class design.

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;

Recommendation

Designers of object oriented classes, then, should avoid:

  1. giving any method a name beginning with get. Just use good old-fashioned function names.

  2. providing 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.

1—We know that standard library classes are now available for this and other common types, but many organizations developed their own before those existed. In any case, this discussion illustrates a problem that persists in many other class definitions today.
2—Some OOP experts strongly favor objects that are immutable. Creating a fresh object will make them happy.

Return to IDI Home Page
Return to Technical articles

Last modified May 12, 2012