📄 ei18.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="EN">
<HEAD>
<title>Effective C++, 2E | Item 18: Strive for class interfaces that are complete and minimal</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "EI18_DIR.HTM";
var dingtext = "Item E18, P";
if (self == top) {
top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="E18: Complete and minimal class interfaces" -->
<A NAME="17774"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EDESGNFR.HTM" TARGET="_top">Design and Declaration</A> <BR> Continue to <A HREF="./EI19_FR.HTM" TARGET="_top">Item 19: Differentiate among member functions, non-member functions, and friend functions.</A></FONT></DIV>
<P><A NAME="dingp1"></A><FONT ID="eititle">Item 18: Strive for class interfaces that are complete and minimal.</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>
<A NAME="5794"></A>
<P><A NAME="dingp2"></A>
The client interface for a class is the interface that is accessible to the programmers who use the class. Typically, only functions exist in this interface, because having data members in the client interface has a number of drawbacks (see <A HREF="./EI20_FR.HTM#5976" TARGET="_top">Item 20</A>).<SCRIPT>create_link(2);</SCRIPT>
</P>
<A NAME="5798"></A>
<P><A NAME="dingp3"></A>
Trying to figure out what functions should be in a class interface can drive you crazy. You're pulled in two completely different directions. On the one hand, you'd like to build a class that is easy to understand, straightforward to use, and easy to implement. That usually implies a fairly small number of member functions, each of which performs a distinct task. On other hand, you'd like your class to be powerful and convenient to use, which often means adding functions to provide support for commonly performed tasks. How do you decide which functions go into the class and which ones <NOBR>don't?<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>
<A NAME="5799"></A>
<P><A NAME="dingp4"></A>
Try this: aim for a class interface that is <I>complete</I> and <I>minimal</I>.<SCRIPT>create_link(4);</SCRIPT>
</P>
<A NAME="5801"></A>
<P><A NAME="dingp5"></A>
A <I>complete</I> interface is one that allows clients to do anything they might reasonably want to do. That is, for any reasonable task that clients might want to accomplish, there is a reasonable way to accomplish it, although it may not be as convenient as clients might like. A <I>minimal</I> interface, on the other hand, is one with as few functions in it as possible, one in which no two member functions have overlapping functionality. If you offer a complete, minimal interface, clients can do whatever they want to do, but the class interface is no more complicated than absolutely <NOBR>necessary.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="5803"></A>
<P><A NAME="dingp6"></A>
The desirability of a complete interface seems obvious enough, but why a minimal interface? Why not just give clients everything they ask for, adding functionality until everyone is <NOBR>happy?<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P>
<A NAME="5804"></A>
<P><A NAME="dingp7"></A>
Aside from the moral issue — is it really <I>right</I> to mollycoddle your clients? — there are definite technical disadvantages to a class interface that is crowded with functions. First, the more functions in an interface, the harder it is for potential clients to understand. The harder it <A NAME="p80"></A>is for them to understand, the more reluctant they will be to learn how to use it. A class with 10 functions looks tractable to most people, but a class with 100 functions is enough to make many programmers run and hide. By expanding the functionality of your class to make it as attractive as possible, you may actually end up discouraging people from learning how to use <NOBR>it.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></P>
<A NAME="5805"></A>
<P><A NAME="dingp8"></A>
A large interface can also lead to confusion. Suppose you create a class that supports cognition for an artificial intelligence application. One of your member functions is called <CODE>think</CODE>, but you later discover that some people want the function to be called <CODE>ponder</CODE>, and others prefer the name <CODE>ruminate</CODE>. In an effort to be accommodating, you offer all three functions, even though they do the same thing. Consider then the plight of a potential client of your class who is trying to figure things out. The client is faced with three different functions, all of which are supposed to do the same thing. Can that really be true? Isn't there some subtle difference between the three, possibly in efficiency or generality or reliability? If not, why are there three different functions? Rather than appreciating your flexibility, such a potential client is likely to wonder what on earth you were thinking (or pondering, or ruminating <NOBR>over).<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<A NAME="5807"></A>
<P><A NAME="dingp9"></A>
A second disadvantage to a large class interface is that of maintenance (see <A HREF="../MEC/MI32_FR.HTM#5373" TARGET="_top">Item M32</A>). It's simply more difficult to maintain and enhance a class with many functions than it is a class with few. It is more difficult to avoid duplicated code (with the attendant duplicated bugs), and it is more difficult to maintain consistency across the interface. It's also more difficult to <NOBR>document.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P>
<A NAME="5808"></A>
<P><A NAME="dingp10"></A>
Finally, long class definitions result in long header files. Because header files typically have to be read every time a program is compiled (see <A HREF="./EI34_FR.HTM#6793" TARGET="_top">Item 34</A>), class definitions that are longer than necessary can incur a substantial penalty in total compile-time over the life of a <NOBR>project.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P>
<A NAME="5812"></A>
<P><A NAME="dingp11"></A>
The long and short of it is that the gratuitous addition of functions to an interface is not without costs, so you need to think carefully about whether the convenience of a new function (a new function can <I>only</I> be added for convenience if the interface is already complete) justifies the additional costs in complexity, comprehensibility, maintainability, and compilation <NOBR>speed.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>
<A NAME="5814"></A>
<P><A NAME="dingp12"></A>
Yet there's no sense in being unduly miserly. It is often justifiable to offer more than a minimal set of functions. If a commonly performed task can be implemented much more efficiently as a member function, that may well justify its addition to the interface. (Then again, it may <A NAME="p81"></A>not. See <A HREF="../MEC/MI16_FR.HTM#40995" TARGET="_top">Item M16</A>.) If the addition of a member function makes the class substantially easier to use, that may be enough to warrant its inclusion in the class. And if adding a member function is likely to prevent client errors, that, too, is a powerful argument for its being part of the <NOBR>interface.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="5818"></A>
<P><A NAME="dingp13"></A>
Consider a concrete example: a template for classes that implement arrays with client-defined upper and lower bounds and that offer optional bounds-checking. The beginning of such an array template is shown <NOBR>below:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="16239"></A>
<UL><PRE>template<class T>
class Array {
public:
enum BoundsCheckingStatus {NO_CHECK_BOUNDS = 0,
CHECK_BOUNDS = 1};
</PRE>
</UL><A NAME="16241"></A>
<UL><PRE> Array( int lowBound, int highBound,
BoundsCheckingStatus check = NO_CHECK_BOUNDS);
</PRE>
</UL><A NAME="16230"></A>
<UL><PRE> Array(const Array& rhs);
</PRE>
</UL><A NAME="16231"></A>
<UL><PRE> ~Array();
</PRE>
</UL><A NAME="5829"></A>
<UL><PRE> Array& operator=(const Array& rhs);
</PRE>
</UL><A NAME="16233"></A>
<UL><PRE>private:
int lBound, hBound; // low bound, high bound
</PRE>
</UL><A NAME="5830"></A>
<UL><PRE>
vector<T> data; // contents of array; see
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -