📄 chap2.htm
字号:
<HTML>
<HEAD><TITLE>A Case Study: Designing a Document Editor</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF">
<A NAME="top"></A>
<A NAME="chapter_scenario"></A>
<P>This chapter presents a case study in the design of a
"What-You-See-Is-What-You-Get" (or "WYSIWYG") document editor called
<STRONG>Lexi</STRONG>.<A NAME="fn1"></A><A HREF="#footnote1"><SUP>1</SUP></A> We'll
see how design patterns capture solutions to design problems in
Lexi and applications like it. By the end of this chapter you will
have gained experience with eight patterns, learning them by
example.</P>
<A NAME="lexi-userint"></A>
<P><A HREF="#editor_Lexi">Figure 2.1</A> depicts Lexi's user interface. A
WYSIWYG representation of the document occupies the large rectangular
area in the center. The document can mix text and graphics freely in
a variety of formatting styles. Surrounding the document are the
usual pull-down menus and scroll bars, plus a collection of page icons
for jumping to a particular page in the document.</P>
<A NAME="editor_Lexi"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/doc.gif"><BR><BR>
Figure 2.1: Lexi's user interface</P>
<A NAME="sec2-1"></A>
<H2><A HREF="#sec2-2"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Design Problems</H2>
<A NAME="auto1000"></A>
<P>We will examine seven problems in Lexi's design:</P>
<OL>
<A NAME="auto1001"></A>
<LI><EM>Document structure.</EM>
The choice of internal representation for the document affects nearly
every aspect of Lexi's design. All editing, formatting, displaying,
and textual analysis will require traversing the representation. The
way we organize this information will impact the design of the rest of
the application.</LI>
<A NAME="auto1002"></A>
<P></P>
<A NAME="auto1003"></A>
<LI><EM>Formatting.</EM>
How does Lexi actually arrange text and graphics into lines and
columns? What objects are responsible for carrying out different
formatting policies? How do these policies interact with the
document's internal representation?</LI>
<A NAME="auto1004"></A>
<P></P>
<A NAME="auto1005"></A>
<LI><EM>Embellishing the user interface.</EM>
Lexi's user interface includes scroll bars, borders, and drop shadows
that embellish the WYSIWYG document interface. Such embellishments are
likely to change as Lexi's user interface evolves. Hence it's
important to be able to add and remove embellishments easily without
affecting the rest of the application.</LI>
<A NAME="auto1006"></A>
<P></P>
<A NAME="lexi-looknfeel"></A>
<A NAME="present-manage"></A>
<LI><EM>Supporting multiple look-and-feel standards.</EM>
Lexi should adapt easily to different look-and-feel standards
such as Motif and Presentation Manager (PM) without major modification.</LI>
<A NAME="auto1007"></A>
<P></P>
<A NAME="multiple-windows"></A>
<LI><EM>Supporting multiple window systems.</EM>
Different look-and-feel standards are usually implemented on different
window systems. Lexi's design should be as independent of the window
system as possible.</LI>
<A NAME="auto1008"></A>
<P></P>
<A NAME="auto1009"></A>
<LI><EM>User operations.</EM>
Users control Lexi through various user interfaces, including
buttons and pull-down menus. The functionality behind these
interfaces is scattered throughout the objects in the application.
The challenge here is to provide a uniform mechanism both for
accessing this scattered functionality and for undoing its effects.</LI>
<A NAME="auto1010"></A>
<P></P>
<A NAME="auto1011"></A>
<LI><EM>Spelling checking and hyphenation.</EM>
How does Lexi support analytical operations such as checking for
misspelled words and determining hyphenation points? How can we
minimize the number of classes we have to modify to add a new
analytical operation?</LI>
</OL>
<A NAME="auto1012"></A>
<P>We discuss these design problems in the sections that follow. Each
problem has an associated set of goals plus constraints on how we
achieve those goals. We explain the goals and constraints in detail
before proposing a specific solution. The problem and its solution
will illustrate one or more design patterns. The discussion for each
problem will culminate in a brief introduction to the relevant
patterns.</P>
<A NAME="sec2-2"></A>
<H2><A HREF="#sec2-3"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Document Structure</H2>
<A NAME="editor_sec_document_structure"></A>
<P>A document is ultimately just an arrangement of basic graphical
elements such as characters, lines, polygons, and other shapes. These
elements capture the total information content of the document. Yet an
author often views these elements not in graphical terms but in terms
of the document's physical structure—lines, columns, figures,
tables, and other substructures.<A NAME="fn2"></A><A HREF="#footnote2"><SUP>2</SUP></A>
In turn, these substructures have substructures of their
own, and so on.</P>
<A NAME="auto1013"></A>
<P>Lexi's user interface should let users manipulate these
substructures directly. For example, a user should be able to treat a
diagram as a unit rather than as a collection of individual graphical
primitives. The user should be able to refer to a table as a whole,
not as an unstructured mass of text and graphics. That helps make the
interface simple and intuitive. To give Lexi's implementation
similar qualities, we'll choose an internal representation that
matches the document's physical structure.</P>
<A NAME="auto1014"></A>
<P>In particular, the internal representation should support the
following:</P>
<UL>
<A NAME="auto1015"></A>
<LI>Maintaining the document's physical structure, that is, the
arrangement of text and graphics into lines, columns, tables, etc.</LI>
<A NAME="auto1016"></A>
<P></P>
<A NAME="auto1017"></A>
<LI>Generating and presenting the document visually.</LI>
<A NAME="auto1018"></A>
<P></P>
<A NAME="auto1019"></A>
<LI>Mapping positions on the display to elements in the internal
representation. This lets Lexi determine what the user is
referring to when he points to something in the visual representation.</LI>
</UL>
<A NAME="auto1020"></A>
<P>In addition to these goals are some constraints. First, we should
treat text and graphics uniformly. The application's interface lets
the user embed text within graphics freely and vice versa. We should
avoid treating graphics as a special case of text or text as a special
case of graphics; otherwise we'll end up with redundant formatting and
manipulation mechanisms. One set of mechanisms should suffice for
both text and graphics.</P>
<A NAME="auto1021"></A>
<P>Second, our implementation shouldn't have to distinguish between
single elements and groups of elements in the internal representation.
Lexi should be able to treat simple and complex elements
uniformly, thereby allowing arbitrarily complex documents. The tenth
element in line five of column two, for instance, could be a single
character or an intricate diagram with many subelements. As long as we
know this element can draw itself and specify its dimensions, its
complexity has no bearing on how and where it should appear on the
page.</P>
<A NAME="auto1022"></A>
<P>Opposing the second constraint, however, is the need to analyze the
text for such things as spelling errors and potential hyphenation
points. Often we don't care whether the element of a line is a simple
or complex object. But sometimes an analysis depends on the objects
being analyzed. It makes little sense, for example, to check the
spelling of a polygon or to hyphenate it. The internal
representation's design should take this and other potentially
conflicting constraints into account.</P>
<A NAME="recursivecomposition"></A>
<H3>Recursive Composition</H3>
<A NAME="auto1023"></A>
<P>A common way to represent hierarchically structured information is
through a technique called <STRONG>recursive composition</STRONG>, which
entails building increasingly complex elements out of simpler ones.
Recursive composition gives us a way to compose a document out of
simple graphical elements. As a first step, we can tile a set of
characters and graphics from left to right to form a line in the
document. Then multiple lines can be arranged to form a column,
multiple columns can form a page, and so on (see
<A HREF="#editor_recursive_composition">Figure 2.2</A>).
<A NAME="editor_recursive_composition"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/textcomp.gif"><BR><BR>
Figure 2.2: Recursive composition of text and graphics</P>
<A NAME="auto1024"></A>
<P>We can represent this physical structure by devoting an object to each
important element. That includes not just the visible elements like
the characters and graphics but the invisible, structural elements as
well—the lines and the column. The result is the object structure
shown in <A HREF="#editor_object_structure">Figure 2.3</A>.</P>
<A NAME="editor_object_structure"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/texts008.gif"><BR><BR>
Figure 2.3: Object structure for recursive composition of
text and graphics</P>
<A NAME="auto1025"></A>
<P>By using an object for each character and graphical element in the
document, we promote flexibility at the finest levels of Lexi's
design. We can treat text and graphics uniformly with respect to how
they are drawn, formatted, and embedded within each other. We can
extend Lexi to support new character sets without disturbing other
functionality. Lexi's object structure mimics the document's
physical structure.</P>
<A NAME="auto1026"></A>
<P>This approach has two important implications. The first is obvious:
The objects need corresponding classes. The second implication, which
may be less obvious, is that these classes must have compatible
interfaces, because we want to treat the objects uniformly. The way to
make interfaces compatible in a language like C++ is to relate the
classes through inheritance.</P>
<A NAME="section_glyphs"></A>
<H3>Glyphs</H3>
<A NAME="auto1027"></A>
<P>We'll define a <STRONG>Glyph</STRONG> abstract class for all
objects that can appear in a document structure.<A NAME="fn3"></A><A
HREF="#footnote3"><SUP>3</SUP></A> Its subclasses define both
primitive graphical elements (like characters and images) and
structural elements (like rows and columns). <A HREF="#editor_glyph_class_hierarchy">Figure 2.4</A> depicts a representative part
of the Glyph class hierarchy, and <A HREF="#editor_basic_glyph_interface">Table 2.1</A> presents the basic glyph interface
in more detail using C++ notation.<A NAME="fn4"></A><A HREF="#footnote4"><SUP>4</SUP></A></P>
<A NAME="editor_glyph_class_hierarchy"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/glyph046.gif"><BR><BR>
Figure 2.4: Partial Glyph class hierarchy</P>
<A NAME="editor_basic_glyph_interface"></A>
<CENTER>
<TABLE
CELLPADDING = 4
BORDER = 1
CELLSPACING = 0
BGCOLOR = #99CCFF
>
<A NAME="auto1028"></A>
<TR>
<TH BGCOLOR=#6699CC ALIGN=CENTER>Responsibility</TH>
<TH BGCOLOR=#6699CC ALIGN=CENTER>Operations</TH>
</TR>
<TR VALIGN=TOP>
<TD ALIGN=CENTER>appearance</TD>
<TD ALIGN=LEFT><CODE>virtual void Draw(Window*)</CODE><BR>
<CODE>virtual void Bounds(Rect&)</CODE></TD>
</TR>
<TR VALIGN=TOP>
<TD ALIGN=CENTER>hit detection</TD>
<TD ALIGN=LEFT><CODE>virtual bool Intersects(const Point&)</CODE></TD>
</TR>
<TR VALIGN=TOP>
<TD ALIGN=CENTER>structure</TD>
<TD ALIGN=LEFT><CODE>virtual void Insert(Glyph*, int)</CODE><BR>
<CODE>virtual void Remove(Glyph*)</CODE><BR>
<CODE>virtual Glyph* Child(int)</CODE><BR>
<CODE>virtual Glyph* Parent()</CODE></TD>
</TR>
</TABLE>
</CENTER>
<P ALIGN=CENTER>Table 2.1: Basic glyph interface</P>
<A NAME="auto1029"></A>
<P>Glyphs have three basic responsibilities. They know (1) how to draw
themselves, (2) what space they occupy, and (3) their children and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -