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

📄 chap2.htm

📁 设计模式英文版 作者:Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides 四人帮的书。 学设计模式的必读的书籍!经典中的经典
💻 HTM
📖 第 1 页 / 共 5 页
字号:
unbordered glyph to draw itself, it should do so without
embellishment. If that glyph is composed in a border, clients
shouldn't have to treat the border containing the glyph any
differently; they just tell it to draw itself as they told the plain
glyph before. This implies that the Border interface matches the Glyph
interface. We subclass Border from Glyph to guarantee this
relationship.</P>

<A NAME="auto1053"></A>
<P>All this leads us to the concept of <STRONG>transparent enclosure</STRONG>,
which combines the notions of (1) single-child (or
single-<STRONG>component</STRONG>) composition and (2) compatible
interfaces.  Clients generally can't tell whether they're dealing with
the component or its <STRONG>enclosure</STRONG> (i.e., the child's parent),
especially if the enclosure simply delegates all its operations to its
component.  But the enclosure can also <EM>augment</EM> the component's
behavior by doing work of its own before and/or after delegating an
operation.  The enclosure can also effectively add state to the
component.  We'll see how next.</P>

<H3>Monoglyph</H3>

<A NAME="auto1054"></A>
<P>We can apply the concept of transparent enclosure to all glyphs that
embellish other glyphs. To make this concept concrete, we'll define a
subclass of Glyph called <STRONG>MonoGlyph</STRONG> to serve as an abstract
class for "embellishment glyphs," like
Border (see <A HREF="#editor_embellish-omt">Figure&nbsp;2.7</A>).
MonoGlyph stores a reference to a component and forwards all requests to
it. That makes MonoGlyph totally transparent to clients by default.
For example, MonoGlyph implements the <CODE>Draw</CODE> operation like this:</P>

<A NAME="auto1055"></A>
<PRE>
    void MonoGlyph::Draw (Window* w) {
        _component->Draw(w);
    }
</PRE>

<A NAME="editor_embellish-omt"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/embel061.gif"><BR><BR>
Figure 2.7:&nbsp;&nbsp;MonoGlyph class relationships</P>

<A NAME="auto1056"></A>
<P>MonoGlyph subclasses reimplement at least one of these forwarding
operations. <CODE>Border::Draw</CODE>, for instance, first invokes the parent
class operation <CODE>MonoGlyph::Draw</CODE> on the component to let the
component do its part&#151;that is, draw everything but the border. Then
<CODE>Border::Draw</CODE> draws the border by calling a private
operation called <CODE>DrawBorder</CODE>, the details of which we'll
omit:</P>

<A NAME="auto1057"></A>
<PRE>
    void Border::Draw (Window* w) {
        MonoGlyph::Draw(w);
        DrawBorder(w);
    }
</PRE>

<A NAME="auto1058"></A>
<P>Notice how <CODE>Border::Draw</CODE> effectively <EM>extends</EM> the parent
class operation to draw the border.  This is in contrast to merely
<EM>replacing</EM> the parent class operation, which would omit the call to
<CODE>MonoGlyph::Draw</CODE>.</P>

<A NAME="scroller"></A>
<P>Another MonoGlyph subclass appears in <A HREF="#editor_embellish-omt">Figure&nbsp;2.7</A>.
<STRONG>Scroller</STRONG> is a MonoGlyph that draws its component in different
locations based on the positions of two scroll bars, which it adds as
embellishments. When Scroller draws its component, it tells the
graphics system to clip to its bounds. Clipping parts of the component
that are scrolled out of view keeps them from appearing on the screen.</P>

<A NAME="auto1059"></A>
<P>Now we have all the pieces we need to add a border and a scrolling
interface to Lexi's text editing area.  We compose the existing
Composition instance in a Scroller instance to add the scrolling
interface, and we compose that in a Border instance.  The resulting
object structure appears in <A HREF="#editor_embellish">Figure&nbsp;2.8</A>.</P>

<A NAME="editor_embellish"></A>
<A NAME="Fig-2.8"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/embel060.gif"><BR><BR>
Figure 2.8:&nbsp;&nbsp;Embellished object structure</P>

<A NAME="auto1060"></A>
<P>Note that we can reverse the order of composition, putting the
bordered composition into the Scroller instance.  In that case the
border would be scrolled along with the text, which may or may not be
desirable.  The point is, transparent enclosure makes it easy to
experiment with different alternatives, and it keeps clients free of
embellishment code.</P>

<A NAME="auto1061"></A>
<P>Note also how the border composes one glyph, not two or more. This is
unlike compositions we've defined so far, in which parent objects were
allowed to have arbitrarily many children. Here, putting a border
around something implies that "something" is singular. We could
assign a meaning to embellishing more than one object at a time, but
then we'd have to mix many kinds of composition in with the notion of
embellishment: row embellishment, column embellishment, and so forth.
That won't help us, since we already have classes to do those kinds of
compositions. So it's better to use existing classes for composition
and add new classes to embellish the result. Keeping embellishment
independent of other kinds of composition both simplifies the
embellishment classes and reduces their number. It also keeps us from
replicating existing composition functionality.</P>

<A NAME="dec-patt"></A>
<H3>Decorator Pattern</H3>

<A NAME="auto1062"></A>
<P>The <A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator&nbsp;(175)</A> pattern captures class and object
relationships that support embellishment by transparent enclosure.
The term "embellishment" actually has broader meaning than what
we've considered here.  In the Decorator pattern, embellishment refers
to anything that adds responsibilities to an object.  We can think
for example of embellishing an abstract syntax tree with semantic
actions, a finite state automaton with new transitions, or a network
of persistent objects with attribute tags.  Decorator generalizes the
approach we've used in Lexi to make it more widely applicable.

<A NAME="sec2-5"></A>
<H2><A HREF="#sec2-6"><IMG SRC="gifsb/down3.gif" BORDER=0></A>
Supporting Multiple Look-and-Feel Standards</H2>

<A NAME="auto1063"></A>
<P>Achieving portability across hardware and software platforms is a
major problem in system design. Retargeting Lexi to a new
platform shouldn't require a major overhaul, or it wouldn't be worth
retargeting. We should make porting as easy as possible.</P>

<A NAME="auto1064"></A>
<P>One obstacle to portability is the diversity of look-and-feel standards,
which are intended to enforce uniformity between applications. These
standards define guidelines for how applications appear and react to the
user. While existing standards aren't that different from each other,
people certainly won't confuse one for the other&#151;Motif applications don't
look and feel exactly like their counterparts on other platforms, and vice
versa. An application that runs on more than one platform must conform to
the user interface style guide on each platform.</P>

<A NAME="auto1065"></A>
<P>Our design goals are to make Lexi conform to multiple existing
look-and-feel standards and to make it easy to add support for new
standards as they (invariably) emerge. We also want our design to
support the ultimate in flexibility: changing Lexi's look and feel
at run-time.</P>


<A NAME="widget"></A>
<H3>Abstracting Object Creation</H3>

<A NAME="auto1066"></A>
<P>Everything we see and interact with in Lexi's user interface is a
glyph composed in other, invisible glyphs like Row and Column. The
invisible glyphs compose visible ones like Button and Character and lay
them out properly. Style guides have much to say about the look and
feel of so-called "widgets," another term for visible glyphs like
buttons, scroll bars, and menus that act as controlling elements in a
user interface.  Widgets might use simpler glyphs such as characters,
circles, rectangles, and polygons to present data.</P>

<A NAME="auto1067"></A>
<P>We'll assume we have two sets of widget glyph classes with which to
implement multiple look-and-feel standards:</P>

<OL>

<A NAME="auto1068"></A>
<LI>A set of abstract Glyph subclasses for each category of widget
glyph. For example, an abstract class ScrollBar will augment the basic
glyph interface to add general scrolling operations; Button is an
abstract class that adds button-oriented operations; and so on.</LI>

<A NAME="auto1069"></A>
<P></P>

<A NAME="present-manage"></A>
<LI>A set of concrete subclasses for each abstract subclass that
implement different look-and-feel standards.  For example, ScrollBar
might have MotifScrollBar and PMScrollBar subclasses that implement
Motif and Presentation Manager-style scroll bars, respectively.</LI>

</OL>

<A NAME="auto1070"></A>
<P>Lexi must distinguish between widget glyphs for different look-and-feel
styles. For example, when Lexi needs to put a button in its interface,
it must instantiate a Glyph subclass for the right style of button
(MotifButton, PMButton, MacButton, etc.).</P>

<A NAME="macintosh1"></A>
<P>It's clear that Lexi's implementation can't do this directly, say,
using a constructor call in C++.  That would hard-code the button of a
particular style, making it impossible to select the style at
run-time.  We'd also have to track down and change every such
constructor call to port Lexi to another platform. And buttons are
only one of a variety of widgets in Lexi's user interface.
Littering our code with constructor calls to specific look-and-feel
classes yields a maintenance nightmare&#151;miss just one, and you could
end up with a Motif menu in the middle of your Mac application.</P>

<A NAME="auto1071"></A>
<P>Lexi needs a way to determine the look-and-feel standard that's being
targeted in order to create the appropriate widgets. Not only must we
avoid making explicit constructor calls; we must also be able to
replace an entire widget set easily. We can achieve both by <EM>abstracting the process of object creation</EM>. An example will
illustrate what we mean.</P>


<H3>Factories and Product Classes</H3>

<A NAME="auto1072"></A>
<P>Normally we might create an instance of a Motif scroll bar glyph with the
following C++ code:</P>

<A NAME="auto1073"></A>
<PRE>
    ScrollBar* sb = new MotifScrollBar;
</PRE>

<A NAME="auto1074"></A>
<P>This is the kind of code to avoid if you want to minimize
Lexi's look-and-feel dependencies.  But suppose we
initialize <CODE>sb</CODE> as follows:</P>

<A NAME="auto1075"></A>
<PRE>
    ScrollBar* sb = guiFactory->CreateScrollBar();
</PRE>

<A NAME="auto1076"></A>
<P>where <CODE>guiFactory</CODE> is an instance of a
<STRONG>MotifFactory</STRONG> class. <CODE>CreateScrollBar</CODE>
returns a new instance of the proper ScrollBar subclass for the
look and feel desired, Motif in this case. As far as clients are
concerned, the effect is the same as calling the MotifScrollBar
constructor directly. But there's a crucial difference:  There's
no longer anything in the code that mentions Motif by name. The
<CODE>guiFactory</CODE> object abstracts the process of creating
not just Motif scroll bars but scroll bars for <EM>any</EM>
look-and-feel standard.  And <CODE>guiFactory</CODE> isn't limited
to producing scroll bars. It can manufacture a full range of widget
glyphs, including scroll bars, buttons, entry fields, menus, and
so forth.</P>

<A NAME="auto1077"></A>
<P>All this is possible because MotifFactory is a subclass of
<STRONG>GUIFactory</STRONG>, an abstract class that defines a
general interface for creating widget glyphs. It includes operations
like <CODE>CreateScrollBar</CODE> and <CODE>CreateButton</CODE>
for instantiating different kinds of widget glyphs. Subclasses of
GUIFactory implement these operations to return glyphs such as
MotifScrollBar and PMButton that implement a particular look and
feel. <A HREF="#editor_factory_hierarchy">Figure&nbsp;2.9</A> shows
the resulting class hierarchy for <CODE>guiFactory</CODE> objects.</P>

<A NAME="editor_factory_hierarchy"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/facto056.gif"><BR><BR>
Figure 2.9:&nbsp;&nbsp;GUIFactory class hierarchy</P>

<A NAME="productobjects"></A>
<P>We say that factories create <STRONG>product</STRONG> objects.
Moreover, the products that a factory produces are related to one
another; in this case, the products are all widgets for the same
look and feel.  <A HREF="#editor_products">Figure&nbsp;2.10</A>
shows some of the product classes needed to make factories work
for widget glyphs.</P>

<A NAME="editor_products"></A>
<P ALIGN=CENTER><IMG SRC="Pictures/produ020.gif"><BR><BR>
Figure 2.10:&nbsp;&nbsp;Abstract product classes and concrete subclasses</P>

<A NAME="auto1078"></A>
<P>The last question we have to answer is, Where does the <CODE>GUIFactory</CODE>
instance come from?   The answer is, Anywhere that's convenient. The
variable <CODE>guiFactory</CODE> could be a global, a static member of a
well-known class, or even a local variable if the entire user interface is
created within one class or function. There's even a design pattern,
<A HREF="pat3efs.htm" TARGET="_mainDisplayFrame">Singleton&nbsp;(127)</A>, for managing well-known, one-of-a-kind
objects like this. The important thing, though, is to initialize
<CODE>guiFactory</CODE> at a point in the program <EM>before</EM> it's ever used
to create widgets but <EM>after</EM> it's clear which look and feel is
desired.</P>

<A NAME="auto1079"></A>
<P>If the look and feel is known at compile-time, then <CODE>guiFactory</CODE>
can be initialized with a simple assignment of a new factory instance
at the beginning of the program:</P>

<A NAME="auto1080"></A>
<PRE>
    GUIFactory* guiFactory = new MotifFactory;
</PRE>

<A NAME="auto1081"></A>
<P>If the user can specify the look and feel with a string name at
startup time, then the code to create the factory might be</P>

<A NAME="auto1082"></A>

⌨️ 快捷键说明

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