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:
interface, and points out a schism in the focus of the C++ and other prominent development communities.
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.
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.
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.
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
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.
Effective C++, Second Edition, by Scott Meyers is at Amazon.com at:
More Effective C++, by Scott Meyers is at Amazon.com at:
Effective STL, by Scott Meyers is at Amazon.com at:
Effective C++ CD, by Scott Meyers is at Amazon.com at:
Scott Meyer's website contains links to many publications, presentations, books, and other information from Scott:
Images of Persephone, the best dog in the world:
Have an opinion? Readers have already posted 3 comments about this article. Why not add yours?
Bill Venners is president of Artima Software, Inc. and editor-in-chief of Artima.com. He is author of the book, Inside the Java Virtual Machine, a programmer-oriented survey of the Java platform's architecture and internals. His popular columns in JavaWorld magazine covered Java internals, object-oriented design, and Jini. Bill has been active in the Jini Community since its inception. He led the Jini Community's ServiceUI project that produced the ServiceUI API. The ServiceUI became the de facto standard way to associate user interfaces to Jini services, and was the first Jini community standard approved via the Jini Decision Process. Bill also serves as an elected member of the Jini Community's initial Technical Oversight Committee (TOC), and in this role helped to define the governance process for the community. He currently devotes most of his energy to building Artima.com into an ever more useful resource for developers.
Artima provides consulting and training services to help you make the most of Scala, reactive and functional programming, enterprise systems, big data, and testing.