Scott Meyers is one of the world's foremost experts on C++ software
development. He wrote the best-selling Effective C++ series
(Addison-Wesley): Effective C++ (1997), More Effective
C++ (1995), and Effective STL (2001); wrote and designed
the Effective C++ CD; is consulting editor for Addison-Wesley's
Effective Software Development Series; and is a member of the advisory board
for Software Development magazine. A programmer since 1972,
Meyers holds an M.S. in computer science from Stanford University and a Ph.D.
from Brown University.

In this four-part interview, which is being published in weekly installments, Meyers gives
his views on many object-oriented design topics:

In Part I: Multiple Inheritance and Interfaces,
Meyers indicates how his view of multiple inheritance has changed with time, describes
the C++ community's take on Java's interface, and points out a schism in
the focus of the C++ and other prominent development communities.

Contracts and Private Data

Bill Venners: Much of my object-oriented design philosophy
originally came from your book Effective C++, but since then I've
somewhat changed the way I think about design. I'm curious how your ideas of
object-oriented design may have changed or evolved since you wrote
Effective C++.

One way my design sense has changed is that I now think of design to a
great extent in terms of contracts. Most people agree that private data is a good
thing. In your Effective C++ guideline, "Avoid data members in the
public interface," you give three justifications for private data. You say private
data provides consistency, because every accessible member is always a
function. You mention private data gives you control over accessibility. Private
data can, for example, be read only. But the "big gun," using your words, is that
private data provides functional abstraction. I could, for example, replace a
function that returns a private data member with a computation that calculates
the same value.

Like a good object-oriented programmer who had read your book, I had
always tried to make my data private. Making data private was the right thing to
do. Nevertheless, in a few cases I had to make some data public.

For example, the Jini API contains a class Entry, whose
subclasses form tuples of objects that you can write into a JavaSpace or Jini
lookup service. The objects that comprise the tuple are held in the
Entry's public fields. Although making non-final fields public is
generally a bad idea, in the Entry case there is a good design
justification for public fields. All those fields must be readable and writable, in
effect public, to facilitate matching serialized forms of individual fields.

On one occasion I designed several Entry subclasses that
contained public fields and at least one method. My experience with these
Entry classes showed me that when data is public, methods
can't promise anything.

I had come to view object-oriented systems as a collection of classes, each
of which takes on a certain set of responsibilities through its interface contract.
These contracts make promises. If you pass an object to the
add method of a Set, for example, that object
should show up when you iterate over the Set. If the data of that
Set is public, however, someone could actually remove that
object without the Set class knowing. So if the data is public, the
class can't make any firm promises in its contract.

Scott Meyers: Exactly. That's completely true. And if I
didn't mention that in the book, shame on me.

Thinking in Contracts

Bill Venners: What about contracts in general? To what
extent do you find it helpful to think in terms of contracts when designing? I don't
mean Bertrand Meyer's Design by Contract, the preconditions, postconditions,
and invariants of the Eiffel language. I mean thinking of design as defining
interfaces that are contracts that make promises to clients. Do you think that's
helpful?

Scott Meyers: I do. I think a contract is inherent in an
interface. An interface says, basically, I will do this if you do this. If you give me
this information, I will perform this computation. If you do that, I will have this
side effect. The contract says, if you call me and give me this stuff, and as long
as you hold up your end of the bargain, this is what I promise in return. I think
that does make a lot of sense, because contracts really are just specifications.

Bill Venners: I agree with you, but not everyone does. I
know many developers who don't think in those terms. It's not the way they
approach design, or the way they put their systems together. They put the
pieces together to make the system work, but they don't find it helpful to think in
terms of formal contracts for each piece.

Scott Meyers: I don't think you can deal with software
systems without having some notion of an agreement between the caller and
the callee. You must have some agreement about who has to do what and who
will do what in response to what. Once you've reached that point, it's just a
matter of naming that understanding. I don't happen to use the word "contract,"
but I'm comfortable with it. I think it is a perfectly natural word to apply, though it
does carry a bit of the Design by Contract baggage.

Minimal and Complete Interfaces

Bill Venners: One guideline in Effective C++
is, "Strive for interfaces that are minimal and complete." How do you know
when an interface is complete? How do you determine what to leave in and
what to leave out?

Scott Meyers: A class is a manifestation of some
concept. You have something in mind. You want to let people talk about that
something. You have some idea of the operations that make sense. You have
some idea of what people might reasonably want to do.

The class is complete when people can do everything that you as a
designer can envision they might reasonably want to do, though they don't
necessarily have to be able to do it in a convenient fashion. If you want to make
things convenient, add some non-member functions that wrap a bunch of calls
to member functions. The class is complete, however, when clients can do
everything you envision people might reasonably want to do.

Bill Venners: But what if I can envision 1000 things?

Scott Meyers: And they're all reasonable?

Bill Venners: Yeah. Or, maybe they aren't but I think they
are. How do I know if 500 of those things really aren't necessary? How do I
decide what is necessary?

Scott Meyers: Necessary is easier than reasonable. A
member function is unnecessary if it can be implemented in terms
of the other member functions. You can provide unlimited functionality. I don't
care about that. The question is whether or not it goes inside the class. If you
have a member function that can be implemented in terms of other member
functions, it is not necessary.

A minimal interface throws out all the unnecessary functions. The
unnecessary functions might simply become non-member functions. In that
case, clients can still use the functions, they just don't call them with member
function syntax.

It's difficult for me to envision a class that has even 100 member functions,
none of which are redundant. For example, one of the more embarrassing
classes in the standard C++ library is the String class. The
String class was designed by committee, and it shows.
String has about 113 member function names, including
overloading. I say member function names because many of them
are member function templates, which means the number of member
functions is literally unlimited. You could easily trim that down to 25 names,
move everything else outside the class, and not lose any functionality. That's
just a matter of a design that went completely crazy.

If you can come up with more than say 25 or 30 member functions, that
strongly suggests you have probably merged more than one concept into a
single class. You should probably think about splitting that class into pieces.

The Minimal Complete Sweet Spot

Bill Venners: These two things, minimal and complete,
seem to be in tension. In your Effective C++ justification of minimal
interfaces, you illustrate that multiple methods that do the same exact thing,
such as think, ponder, and
ruminate, are confusing. But striving for a minimal interface
also seems to discourage the adding of gratuitous bells and whistles.

Scott Meyers: Yes.

Bill Venners: I guess I'm going back to the question, how
do I know if the client really needs a particular method? How do you find that
sweet spot that is both minimal and complete?

Scott Meyers: Finding the sweet spot is harder. If you
follow my design guideline, most classes will probably be harder to use than
you would like. Many convenience functions that people want won�t be there. If
you want to have a nicely rounded class interface that does hit the sweet spot,
you will almost certainly have some ancillary member functions. The ancillary
functions will let people do things they could have done by making a bunch of
calls through the public interface. Instead, they just call a single function that
wraps the bunch of calls into a single function name.

Bill Venners: But that's not the minimal and complete
sweet spot. That's the almost minimal, a little more than complete, but user-
friendly spot.

Scott Meyers: Minimal and complete applies to the
member functions. In that particular item, I'm talking about what you make
member functions. I advocate that you determine what should be a member
function by shooting for the minimal complete set.

However, you will probably want to offer clients a greater degree of
functionality. I will call this greater degree of functionality, which includes all the
member functions plus whatever non-member functions you provide for
convenience, the interface to the class, because that is the set of
functionality you are offering them. When you're shooting for the sweet spot, you
really want a class that has a convenient interface.

Bill Venners: I see. Where are those non-member
functions? Are they global functions that take this type of object as parameters?

Scott Meyers: Exactly. They are non-member functions. I
actually wrote an article for CUJ (C/C++ Users Journal) a couple
years ago. I suggest putting the non-member functions in the same
namespace as the class, so they are also not global. I suggest using the
namespace to package the non-member functions with the classes to which
they are associated.

Talk Back!

Have an opinion about contracts, private data, or minimal and complete interfaces? Discuss this article in the News & Ideas Forum topic,
Designing Contracts and Interfaces