📄 ch18.htm
字号:
attached files. This tells you that you'll need accessor methods for each of these
attributes, as well as methods to report on the size of the attached files, the size
of the messages, and so forth.</P>
<P>Some of the services to which you will connect will use rich text--that is, text
with formatting instructions to set the font, character size, and attributes, such
as bold and italic. Other services do not support these attributes, and those that
do may or may not use their own proprietary scheme for managing rich text. Your class
will need conversion methods for turning rich text into plain ASCII, and perhaps
for turning other formats into PostMaster formats.
<H4 ALIGN="CENTER"><A NAME="Heading24"></A><FONT COLOR="#000077">Application Program
Interface</FONT></H4>
<P>An Application Program Interface (API) is a set of documentation and routines
for using a service. Many of the mail providers will give you an API so that PostMaster
mail will be able to take advantage of their more advanced features, such as rich
text and embedding files. PostMaster will also want to publish its own API so that
other providers can plan for working with PostMaster in the future.</P>
<P>Your <TT>PostMasterMessage</TT> class will want to have a well-designed public
interface, and the conversion functions will be a principal component of PostMaster's
API. Listing 18.2 illustrates what <TT>PostMasterMessage</TT>'s interface looks like
so far.</P>
<P><A NAME="Heading25"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 18.2. PostMasterMessages
interface</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1: class PostMasterMessage : public MailMessage
2: {
3: public:
4: PostMasterMessage();
5: PostMasterMessage(
6: pAddress Sender,
7: pAddress Recipient,
8: pString Subject,
9: pDate creationDate);
10:
11: // other constructors here
12: // remember to include copy constructor
13: // as well as constructor from storage
14: // and constructor from wire format
15: // Also include constructors from other formats
16: ~PostMasterMessage();
17: pAddress& GetSender() const;
18: void SetSender(pAddress&);
19: // other member accessors
20:
21: // operator methods here, including operator equals
22: // and conversion routines to turn PostMaster messages
23: // into messages of other formats.
24:
25: private:
26: pAddress itsSender;
27: pAddress itsRecipient;
28: pString itsSubject;
29: pDate itsCreationDate;
30: pDate itsLastModDate;
31: pDate itsReceiptDate;
32: pDate itsFirstReadDate;
33: pDate itsLastReadDate;
<TT>34: };</TT></FONT></PRE>
<P><FONT COLOR="#000000"><B><BR>
Output: </B></FONT>None. <BR>
<FONT COLOR="#000077"><B><BR>
Analysis: </B></FONT>Class <TT>PostMasterMessage</TT> is declared to derive from
<TT>MailMessage</TT>. A number of constructors will be provided, facilitating the
creation of <TT>PostMasterMessage</TT>s from other types of mail messages.</P>
<P>A number of accessor methods are anticipated for reading and setting the various
member data, as well as operators for turning all or part of this message into other
message formats. You anticipate storing these messages to disk and reading them from
the wire, so accessor methods are needed for those purposes as well.
<H4 ALIGN="CENTER"><A NAME="Heading27"></A><FONT COLOR="#000077">Programming in Large
Groups</FONT></H4>
<P>Even this preliminary architecture is enough to indicate how the various development
groups ought to proceed. The communications group can go ahead and start work on
the communications back end, negotiating a narrow interface with the message format
group.</P>
<P>The message format group will probably lay out the general interface to the <TT>Message</TT>
classes, as was begun above, and then will turn its attention to the question of
how to write data to the disk and read it back. Once this disk interface is well
understood, they will be in a good position to negotiate the interface to the communications
layer.</P>
<P>The message editors will be tempted to create editors with an intimate knowledge
of the internals of the <TT>Message</TT> class, but this would be a bad design mistake.
They too must negotiate a very narrow interface to the <TT>Message</TT> class; message
editor objects should know very little about the internal structure of messages.
<H4 ALIGN="CENTER"><A NAME="Heading28"></A><FONT COLOR="#000077">Ongoing Design Considerations</FONT></H4>
<P>As the project continues, you will repeatedly confront this basic design issue:
In which class should you put a given set of functionality (or information)? Should
the <TT>Message</TT> class have this function, or should the <TT>Address</TT> class?
Should the editor store this information, or should the message store it itself?</P>
<P>Your classes should operate on a "need to know" basis, much like secret
agents. They shouldn't share any more knowledge than is absolutely necessary.
<H3 ALIGN="CENTER"><A NAME="Heading29"></A><FONT COLOR="#000077">Design Decisions</FONT></H3>
<P>As you progress with your program, you will face hundreds of design issues. They
will range from the more global questions, "What do we want this to do?"
to the more specific, "How do we make this work?"</P>
<P>While the details of your implementation won't be finalized until you ship the
code, and some of the interfaces will continue to shift and change as you work, you
must ensure that your design is well understood early in the process. It is imperative
that you know what you are trying to build before you write the code. The single
most frequent cause of software dying on the vine must be that there was not sufficient
agreement early enough in the process about what was being built.
<H4 ALIGN="CENTER"><A NAME="Heading30"></A><FONT COLOR="#000077">Decisions, Decisions</FONT></H4>
<P>To get a feel for what the design process is like, examine this question, "What
will be on the menu?" For PostMaster, the first choice is probably "new
mail message," and this immediately raises another design issue: When the user
presses <TT>New Message</TT>, what happens? Does an editor get created, which in
turn creates a mail message, or does a new mail message get created, which then creates
the editor?</P>
<P>The command you are working with is "new mail message," so creating
a new mail message seems like the obvious thing to do. But what happens if the user
hits Cancel after starting to write the message? Perhaps it would be cleaner to first
create the editor and have it create (and own) the new message.</P>
<P>The problem with this approach is that the editor will need to act differently
if it is creating a message than if it is editing the message, whereas if the message
is created first and then handed to the editor, only one set of code need exist:
Everything is an edit of an existing message.</P>
<P>If a message is created first, who creates it? Is it created by the menu command
code? If so, does the menu also tell the message to edit itself, or is this part
of the constructor method of the message?</P>
<P>It makes sense for the constructor to do this at first glance; after all, every
time you create a message you'll probably want to edit it. Nonetheless, this is not
a good design idea. First, it is very possible that the premise is wrong: You may
well create "canned" messages (that is, error messages mailed to the system
operator) that are not put into an editor. Second, and more important, a constructor's
job is to create an object; it should do no more and no less than that. Once a mail
message is created, the constructor's job is done; adding a call to the edit method
just confuses the role of the constructor and makes the mail message vulnerable to
failures in the editor.</P>
<P>What is worse, the edit method will call another class, the editor, causing its
constructor to be called. Yet the editor is not a base class of the message, nor
is it contained within the message; it would be unfortunate if the construction of
the message depended on successful construction of the editor.</P>
<P>Finally, you won't want to call the editor at all if the message can't be successfully
created; yet successful creation would, in this scenario, depend on calling the editor!
Clearly you want to fully return from the message's constructor before calling <TT>Message::Edit()</TT>.
<BLOCKQUOTE>
<P>
<HR>
<B>DO </B>look for objects that arise naturally out of your design.<B> DO</B> redesign
as your understanding of the problem space improves.<B> DON'T</B> share more information
among the classes than is absolutely necessary. <B>DO</B> look for opportunities
to take advantage of C++'s polymorphism.
<HR>
</BLOCKQUOTE>
<H3 ALIGN="CENTER"><A NAME="Heading31"></A><FONT COLOR="#000077">Working with Driver
Programs</FONT></H3>
<P>One approach to surfacing design issues is to create a driver program early in
the process. For example, the driver program for PostMaster might offer a very simple
menu, which will create <TT>PostMasterMessage</TT> objects, manipulate them, and
otherwise exercise some of the design.</P>
<DL>
<DD>
<HR>
<FONT COLOR="#000077"><B>New Term:</B></FONT><B> </B>A <I>driver program</I> is a
function that exists only to demonstrate or test other functions.
<HR>
</DL>
<P>Listing 18.3 illustrates a somewhat more robust definition of the <TT>PostMasterMessage</TT>
class and a simple driver program.</P>
<P><A NAME="Heading32"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 18.3. A driver
program for PostMasterMessage.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">1: #include <iostream.h>
2: #include <string.h>
3:
4: typedef unsigned long pDate;
5: enum SERVICE
6: { PostMaster, Interchange, CompuServe, Prodigy, AOL, Internet };
7: class String
8: {
9: public:
10: // constructors
11: String();
12: String(const char *const);
13: String(const String &);
14: ~String();
15:
16: // overloaded operators
17: char & operator[](int offset);
18: char operator[](int offset) const;
19: String operator+(const String&);
20: void operator+=(const String&);
21: String & operator= (const String &);
22: friend ostream& operator<<
23: ( ostream& theStream,String& theString);
24: // General accessors
25: int GetLen()const { return itsLen; }
26: const char * GetString() const { return itsString; }
27: // static int ConstructorCount;
28: private:
29: String (int); // private constructor
30: char * itsString;
31: unsigned short itsLen;
32:
33: };
34:
35: // default constructor creates string of 0 bytes
36: String::String()
37: {
38: itsString = new char[1];
39: itsString[0] = `\0';
40: itsLen=0;
41: // cout << "\tDefault string constructor\n";
42: // ConstructorCount++;
43: }
44:
45: // private (helper) constructor, used only by
46: // class methods for creating a new string of
47: // required size. Null filled.
48: String::String(int len)
49: {
50: itsString = new char[len+1];
51: for (int i = 0; i<=len; i++)
52: itsString[1] = `\0';
53: itsLen=len;
54: // cout << "\tString(int) constructor\n";
55: // ConstructorCount++;
56: }
57:
58: // Converts a character array to a String
59: String::String(const char * const cString)
60: {
61: itsLen = strlen(cString);
62: itsString = new char[itsLen+1];
63: for (int i = 0; i<itsLen; i++)
64: itsString[i] = cString[i];
65: itsString[itsLen]='\0';
66: // cout << "\tString(char*) constructor\n";
67: // ConstructorCount++;
68: }
69:
70: // copy constructor
71: String::String (const String & rhs)
72: {
73: itsLen=rhs.GetLen();
74: itsString = new char[itsLen+1];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -