⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap2.htm

📁 设计模式英文版 作者:Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides 四人帮的书。 学设计模式的必读的书籍!经典中的经典
💻 HTM
📖 第 1 页 / 共 5 页
字号:
parent.</P>

<A NAME="window"></A>
<P>Glyph subclasses redefine the <CODE>Draw</CODE> operation to render
themselves onto a window. They are passed a reference to a <CODE>Window</CODE>
object in the call to <CODE>Draw</CODE>. The <STRONG>Window</STRONG> class defines
graphics operations for rendering text and basic shapes in a window on the
screen.  A <STRONG>Rectangle</STRONG> subclass of Glyph might redefine
<CODE>Draw</CODE> as follows:</P>

<A NAME="auto1030"></A>
<PRE>
    void Rectangle::Draw (Window* w) {
        w->DrawRect(_x0, _y0, _x1, _y1);
    }
</PRE>

<A NAME="auto1031"></A>
<P>where <CODE>_x0</CODE>, <CODE>_y0</CODE>, <CODE>_x1</CODE>, and <CODE>_y1</CODE>
are data members of <CODE>Rectangle</CODE> that define two opposing corners of
the rectangle.  <CODE>DrawRect</CODE> is the Window operation that makes
the rectangle appear on the screen.

<A NAME="auto1032"></A>
<P>A parent glyph often needs to know how much space a child glyph occupies,
for example, to arrange it and other glyphs in a line so that none overlaps
(as shown in <A HREF="#editor_recursive_composition">Figure&nbsp;2.3</A>). The
<CODE>Bounds</CODE> operation returns the rectangular area that the glyph
occupies. It returns the opposite corners of the smallest rectangle that
contains the glyph. Glyph subclasses redefine this operation to return the
rectangular area in which they draw.</P>

<A NAME="auto1033"></A>
<P>The <CODE>Intersects</CODE> operation returns whether a specified point
intersects the glyph.  Whenever the user clicks somewhere in the
document, Lexi calls this operation to determine which glyph or
glyph structure is under the mouse.  The Rectangle class redefines
this operation to compute the intersection of the rectangle and the
given point.</P>

<A NAME="auto1034"></A>
<P>Because glyphs can have children, we need a common interface to
add, remove, and access those children. For example, a Row's children
are the glyphs it arranges into a row. The <CODE>Insert</CODE>
operation inserts a glyph at a position specified by an integer
index.<A NAME="fn5"></A><A HREF="#footnote5"><SUP>5</SUP></A> The <CODE>Remove</CODE>
operation removes a specified glyph if it is indeed a child.</P>

<A NAME="auto1035"></A>
<P>The <CODE>Child</CODE> operation returns the child (if any) at the given
index. Glyphs like Row that can have children should use <CODE>Child</CODE>
internally instead of accessing the child data structure directly. That way
you won't have to modify operations like <CODE>Draw</CODE> that iterate
through the children when you change the data structure from, say, an array
to a linked list. Similarly, <CODE>Parent</CODE> provides a standard interface
to the glyph's parent, if any. Glyphs in Lexi store a reference to
their parent, and their <CODE>Parent</CODE> operation simply returns this
reference.</P>


<H3>Composite Pattern</H3>

<A NAME="auto1036"></A>
<P>Recursive composition is good for more than just documents. We can use
it to represent any potentially complex, hierarchical structure.  The
<A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite&nbsp;(163)</A> pattern captures the essence of
recursive composition in object-oriented terms. Now would be a good
time to turn to that pattern and study it, referring back to this
scenario as needed.</P>

<A NAME="sec2-3"></A>
<H2><A HREF="#sec2-4"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Formatting</H2>

<A NAME="editor_sec_formatting"></A>
<P>We've settled on a way to <EM>represent</EM> the document's physical
structure. Next, we need to figure out how to construct a <EM>particular</EM> physical structure, one that corresponds to a properly
formatted document.  Representation and formatting are distinct: The
ability to capture the document's physical structure doesn't tell us
how to arrive at a particular structure. This responsibility rests
mostly on Lexi. It must break text into lines, lines into columns,
and so on, taking into account the user's higher-level desires. For
example, the user might want to vary margin widths, indentation, and
tabulation; single or double space; and probably many other formatting
constraints.<A NAME="fn6"></A><A HREF="#footnote6"><SUP>6</SUP></A>
Lexi's
formatting algorithm must take all of these into account.</P>

<A NAME="auto1037"></A>
<P>By the way, we'll restrict "formatting" to mean breaking a collection of
glyphs into lines. In fact, we'll use the terms "formatting" and
"linebreaking" interchangeably. The techniques we'll discuss apply
equally well to breaking lines into columns and to breaking columns into
pages.</P>


<H3>Encapsulating the Formatting Algorithm</H3>

<A NAME="auto1038"></A>
<P>The formatting process, with all its constraints and details, isn't easy to
automate. There are many approaches to the problem, and people have come up
with a variety of formatting algorithms with different strengths and
weaknesses. Because Lexi is a WYSIWYG editor, an important trade-off to
consider is the balance between formatting quality and formatting speed. We
want generally good response from the editor without sacrificing how good
the document looks. This trade-off is subject to many factors, not all of
which can be ascertained at compile-time. For example, the user might
tolerate slightly slower response in exchange for better formatting. That
trade-off might make an entirely different formatting algorithm more
appropriate than the current one. Another, more implementation-driven
trade-off balances formatting speed and storage requirements: It may be
possible to decrease formatting time by caching more information.</P>

<A NAME="auto1039"></A>
<P>Because formatting algorithms tend to be complex, it's also desirable
to keep them well-contained or&#151;better yet&#151;completely independent
of the document structure. Ideally we could add a new kind of Glyph
subclass without regard to the formatting algorithm. Conversely,
adding a new formatting algorithm shouldn't require modifying existing
glyphs.</P>

<A NAME="auto1040"></A>
<P>These characteristics suggest we should design Lexi so that it's
easy to change the formatting algorithm at least at compile-time, if
not at run-time as well. We can isolate the algorithm and make it
easily replaceable at the same time by encapsulating it in an object.
More specifically, we'll define a separate class hierarchy for objects
that encapsulate formatting algorithms. The root of the hierarchy will
define an interface that supports a wide range of formatting
algorithms, and each subclass will implement the interface to carry
out a particular algorithm. Then we can introduce a Glyph subclass
that will structure its children automatically using a given algorithm
object.</P>

<H3>Compositor and Composition</H3>

<A NAME="auto1041"></A>
<P>We'll define a <STRONG>Compositor</STRONG> class for objects
that can encapsulate a formatting algorithm. The interface (<A HREF="#editor_basic_compositor_interface">Table&nbsp;2.2</A>) lets
the compositor know <EM>what</EM> glyphs to format and <EM>when</EM>
to do the formatting.  The glyphs it formats are the children of
a special Glyph subclass called <STRONG>Composition</STRONG>. A
composition gets an instance of a Compositor subclass (specialized
for a particular linebreaking algorithm) when it is created, and
it tells the compositor to <CODE>Compose</CODE> its glyphs when
necessary, for example, when the user changes a document.
<A HREF="#editor_composition_and_compositor_class_relationships">Figure&nbsp;2.5</A>
depicts the relationships between the Composition and Compositor classes.</P>

<A NAME="editor_basic_compositor_interface"></A>
<CENTER>
<TABLE
        CELLPADDING     = 4
        BORDER          = 1
        CELLSPACING     = 0
        BGCOLOR         = #99CCFF
>

<A NAME="auto1042"></A>
<TR>
<TH BGCOLOR=#6699CC ALIGN=CENTER>Responsibility</TH>
<TH BGCOLOR=#6699CC ALIGN=CENTER>Operations</TH>
</TR>

<A NAME="auto1043"></A>
<TR>
<TD>what to format</TD>
<TD><CODE>void SetComposition(Composition*)</CODE></TD>
</TR>

<A NAME="auto1044"></A>
<TR>
<TD>when to format</TD>
<TD><CODE>virtual void Compose()</CODE></TD>
</TR>

</TABLE>

</CENTER>

<P ALIGN=CENTER>Table 2.2&nbsp;&nbsp;Basic compositor interface</P>

<A NAME="42c"></A>
<A NAME="editor_composition_and_compositor_class_relationships"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/compo071.gif"><BR><BR>
Figure 2.5:&nbsp;&nbsp;Composition and Compositor class relationships</P>

<A NAME="auto1045"></A>
<P>An unformatted Composition object contains only the visible
glyphs that make up the document's basic content. It doesn't contain
glyphs that determine the document's physical structure, such as
Row and Column.  The composition is in this state just after it's
created and initialized with the glyphs it should format.  When
the composition needs formatting, it calls its compositor's
<CODE>Compose</CODE> operation. The compositor in turn iterates
through the composition's children and inserts new Row and Column
glyphs according to its linebreaking algorithm.<A NAME="fn7"></A><A
HREF="#footnote7"><SUP>7</SUP></A> <A HREF="#editor_compositor_object_structure">Figure&nbsp;2.6</A> shows the resulting object
structure.  Glyphs that the compositor created and inserted into
the object structure appear with gray backgrounds in the figure.</P>

<A NAME="editor_compositor_object_structure"></A>
<A NAME="simple-compositor-42c"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/compo070.gif"><BR><BR>
Figure 2.6:&nbsp;&nbsp;Object structure reflecting
compositor-directed linebreaking</P>

<A NAME="document-color"></A>
<A NAME="simple-compositor"></A>
<A NAME="tex"></A>
<P>Each Compositor subclass can implement a different linebreaking algorithm.
For example, a SimpleCompositor might do a quick pass without regard for
such esoterica as the document's "color." Good color means having an even
distribution of text and whitespace. A TeXCompositor would implement the
full TeX algorithm&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=tex" TARGET="_mainDisplayFrame">Knu84</A>], which takes things like color into account
in exchange for longer formatting times.</P>

<A NAME="auto1046"></A>
<P>The Compositor-Composition class split ensures a strong separation
between code that supports the document's physical structure and the
code for different formatting algorithms. We can add new Compositor
subclasses without touching the glyph classes, and vice versa. In
fact, we can change the linebreaking algorithm at run-time by adding a
single <CODE>SetCompositor</CODE> operation to Composition's basic glyph
interface.</P>

<A NAME="strat-lexi"></A>
<H3>Strategy Pattern</H3>

<A NAME="auto1047"></A>
<P>Encapsulating an algorithm in an object is the intent of the
<A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy&nbsp;(315)</A> pattern. The key participants in the
pattern are Strategy objects (which encapsulate different algorithms)
and the context in which they operate.  Compositors are strategies;
they encapsulate different formatting algorithms. A composition is the
context for a compositor strategy.</P>

<A NAME="auto1048"></A>
<P>The key to applying the Strategy pattern is designing interfaces for
the strategy and its context that are general enough to support a
range of algorithms. You shouldn't have to change the strategy or
context interface to support a new algorithm. In our example, the
basic Glyph interface's support for child access, insertion, and
removal is general enough to let Compositor subclasses change the
document's physical structure, regardless of the algorithm they use to
do it. Likewise, the Compositor interface gives compositions whatever
they need to initiate formatting.</P>

<A NAME="sec2-4"></A>
<H2><A HREF="#sec2-5"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Embellishing the User Interface</H2>

<A NAME="auto1049"></A>
<P>We consider two embellishments in Lexi's user interface. The
first adds a border around the text editing area to demarcate the page
of text.  The second adds scroll bars that let the user view different
parts of the page. To make it easy to add and remove these
embellishments (especially at run-time), we shouldn't use inheritance
to add them to the user interface. We achieve the most flexibility
if other user interface objects don't even know the embellishments are
there. That will let us add and remove the embellishments without
changing other classes.</P>

<A NAME="transparentencl"></A>
<H3>Transparent Enclosure</H3>

<A NAME="auto1050"></A>
<P>From a programming point of view, embellishing the user interface involves
extending existing code. Using inheritance to do such extension precludes
rearranging embellishments at run-time, but an equally serious problem is
the explosion of classes that can result from an inheritance-based
approach.</P>

<A NAME="compcomposite"></A>
<P>We could add a border to Composition by subclassing it to yield a
BorderedComposition class.  Or we could add a scrolling interface in
the same way to yield a ScrollableComposition.  If we want both scroll
bars and a border, we might produce a BorderedScrollableComposition,
and so forth. In the extreme, we end up with a class for every
possible combination of embellishments, a solution that quickly
becomes unworkable as the variety of embellishments grows.</P>

<A NAME="auto1051"></A>
<P>Object composition offers a potentially more workable and flexible
extension mechanism.  But what objects do we compose?  Since we know
we're embellishing an existing glyph, we could make the embellishment
itself an object (say, an instance of class <STRONG>Border</STRONG>). That
gives us two candidates for composition, the glyph and the border. The
next step is to decide who composes whom. We could have the border
contain the glyph, which makes sense given that the border will
surround the glyph on the screen. Or we could do the opposite&#151;put
the border into the glyph&#151;but then we must make modifications to the
corresponding Glyph subclass to make it aware of the border. Our first
choice, composing the glyph in the border, keeps the border-drawing
code entirely in the Border class, leaving other classes alone.</P>

<A NAME="auto1052"></A>
<P>What does the Border class look like? The fact that borders have an
appearance suggests they should actually be glyphs; that is, Border
should be a subclass of Glyph. But there's a more compelling reason
for doing this: Clients shouldn't care whether glyphs have borders or
not. They should treat glyphs uniformly. When clients tell a plain,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -