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

📄 pat4d-1.htm

📁 四人帮《设计模式》一书英文版本
💻 HTM
📖 第 1 页 / 共 2 页
字号:
costly to apply.  In the Strategy pattern, the component forwards some
of its behavior to a separate strategy object.  The Strategy pattern
lets us alter or extend the component's functionality by replacing the
strategy object.</P>

<A NAME="auto1045"></A>
<P>For example, we can support different border styles by having the
component defer border-drawing to a separate Border object.  The
Border object is a Strategy object that encapsulates a border-drawing
strategy. By extending the number of strategies from just one to an
open-ended list, we achieve the same effect as nesting decorators
recursively.</P>

<A NAME="adorner"></A>
<A NAME="bedrock1"></A>
<A NAME="macapp-dec"></A>
<P>In MacApp 3.0 [<A HREF="bibfs-1.htm#macapp" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#macapp" TARGET="_mainDisplayFrame">App89</A>] and Bedrock [<A HREF="bibfs-1.htm#bedrock" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#bedrock" TARGE
T="_mainDisplayFrame">Sym93a</A>], for example,
graphical components (called "views") maintain a list of "adorner"
objects that can attach additional adornments like borders to a view
component.  If a view has any adorners attached, then it gives them a
chance to draw additional embellishments.  MacApp and Bedrock must
use this approach because the View class is heavyweight.  It would be
too expensive to use a full-fledged View just to add a border.</P>

<A NAME="auto1046"></A>
<P>Since the Decorator pattern only changes a component from the outside,
the component doesn't have to know anything about its decorators; that
is, the decorators are transparent to the component:</P>

<A NAME="dec-180o"></A>
<P ALIGN=CENTER><IMG SRC="deco-069-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/deco-069.gif"></P>

<A NAME="auto1047"></A>
<P>With strategies, the component itself knows about possible extensions.
So it has to reference and maintain the corresponding strategies:</P>

<A NAME="strat-180o"></A>
<P ALIGN=CENTER><IMG SRC="deco-068-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/deco-068.gif"></P>

<A NAME="dec-180"></A>
<P>The Strategy-based approach might require modifying the component to
accommodate new extensions.  On the other hand, a strategy can have
its own specialized interface, whereas a decorator's interface must
conform to the component's. A strategy for rendering a border, for
example, need only define the interface for rendering a border
(DrawBorder, GetWidth, etc.), which means that the strategy can be
lightweight even if the Component class is heavyweight.</P>

<A NAME="bedrock2"></A>
<A NAME="macapp-dec2"></A>
<P>MacApp and Bedrock use this approach for more than just adorning
views. They also use it to augment the event-handling behavior of
objects.  In both systems, a view maintains a list of "behavior"
objects that can modify and intercept events. The view gives each of
the registered behavior objects a chance to handle the event before
nonregistered behaviors, effectively overriding them.  You can
decorate a view with special keyboard-handling support, for example,
by registering a behavior object that intercepts and handles key
events.</P>

</OL>

<A NAME="samplecode"></A>
<H2><A HREF="#knownuses"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next: 
Known Uses"></A> Sample Code</H2> 

<A NAME="auto1048"></A>
<P>The following code shows how to implement user interface decorators in
C++.  We'll assume there's a Component class called
<CODE>VisualComponent</CODE>.</P>

<A NAME="auto1049"></A>
<PRE>
    class VisualComponent {
    public:
        VisualComponent();
    
        virtual void Draw();
        virtual void Resize();
        // ...
    };
</PRE>

<A NAME="auto1050"></A>
<P>We define a subclass of <CODE>VisualComponent</CODE> called
<CODE>Decorator</CODE>, which we'll subclass to obtain different
decorations.</P>

<A NAME="auto1051"></A>
<PRE>
    class Decorator : public VisualComponent {
    public:
        Decorator(VisualComponent*);
    
        virtual void Draw();
        virtual void Resize();
        // ...
    private:
        VisualComponent* _component;
    };
</PRE>


<A NAME="auto1052"></A>
<P><CODE>Decorator</CODE> decorates the <CODE>VisualComponent</CODE>
referenced by the <CODE>_component</CODE> instance variable, which is
initialized in the constructor.  For each operation in
<CODE>VisualComponent</CODE>'s interface, <CODE>Decorator</CODE> defines a
default implementation that passes the request on to
<CODE>_component</CODE>:</P>

<A NAME="auto1053"></A>
<PRE>
    void Decorator::Draw () {
        _component->Draw();
    }
    
    void Decorator::Resize () {
        _component->Resize();
    }
</PRE>

<A NAME="auto1054"></A>
<P>Subclasses of <CODE>Decorator</CODE> define specific decorations.  For
example, the class <CODE>BorderDecorator</CODE> adds a border to its
enclosing component.  <CODE>BorderDecorator</CODE> is a subclass of
<CODE>Decorator</CODE> that overrides the <CODE>Draw</CODE> operation
to draw the border.  <CODE>BorderDecorator</CODE> also defines a private
<CODE>DrawBorder</CODE> helper operation that does the drawing.  The
subclass inherits all other operation implementations from
<CODE>Decorator</CODE>.</P>

<A NAME="auto1055"></A>
<PRE>
    class BorderDecorator : public Decorator {
    public:
        BorderDecorator(VisualComponent*, int borderWidth);
    
        virtual void Draw();
    private:
        void DrawBorder(int);
    private:
        int _width;
    };
    
    void BorderDecorator::Draw () {
        Decorator::Draw();
        DrawBorder(_width);
    }
</PRE>

<A NAME="auto1056"></A>
<P>A similar implementation would follow for <CODE>ScrollDecorator</CODE> and
<CODE>DropShadowDecorator</CODE>, which would add scrolling and drop shadow
capabilities to a visual component.</P>

<A NAME="auto1057"></A>
<P>Now we can compose instances of these classes to provide different
decorations.  The following code illustrates how we can use
decorators to create a bordered scrollable <CODE>TextView</CODE>.</P>

<A NAME="auto1058"></A>
<P>First, we need a way to put a visual component into a window object.
We'll assume our <CODE>Window</CODE> class provides a
<CODE>SetContents</CODE> operation for this purpose:</P>

<A NAME="auto1059"></A>
<PRE>
    void Window::SetContents (VisualComponent* contents) {
        // ...
    }
</PRE>

<A NAME="auto1060"></A>
<P>Now we can create the text view and a window to put it in:</P>

<A NAME="auto1061"></A>
<PRE>
    Window* window = new Window;
    TextView* textView = new TextView;
</PRE>


<A NAME="auto1062"></A>
<P><CODE>TextView</CODE> is a <CODE>VisualComponent</CODE>, which lets us put it
into the window:</P>

<A NAME="auto1063"></A>
<PRE>
    window->SetContents(textView);
</PRE>

<A NAME="auto1064"></A>
<P>But we want a bordered and scrollable <CODE>TextView</CODE>.  So we
decorate it accordingly before putting it in the window.</P>

<A NAME="auto1065"></A>
<PRE>
    window->SetContents(
        new BorderDecorator(
            new ScrollDecorator(textView), 1
        )
    );
</PRE>

<A NAME="auto1066"></A>
<P>Because <CODE>Window</CODE> accesses its contents through the
<CODE>VisualComponent</CODE> interface, it's unaware of the decorator's
presence.  You, as the client, can still keep track of the text view if you
have to interact with it directly, for example, when you need to
invoke operations that aren't part of the <CODE>VisualComponent</CODE>
interface.  Clients that rely on the component's identity should refer
to it directly as well.</P>

<A NAME="knownuses"></A>
<H2><A HREF="#relatedpatterns"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 
ALT="next: Related Patterns"></A> Known Uses</H2> 

<A NAME="debuggingglyph"></A>
<P>Many object-oriented user interface toolkits use decorators to add
graphical embellishments to widgets.  Examples include
InterViews [<A HREF="bibfs-1.htm#interviews_composition" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#interviews_composition" TARGET="_mainDisplayFrame">LVC98</A>, <A HREF="bibfs-1.htm#InterViews3.1" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#InterViews3.1" TARGET=
"_mainDisplayFrame">LCI+92</A>],
ET++ [<A HREF="bibfs-1.htm#et++" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>], and the
ObjectWorks\Smalltalk class library [<A HREF="bibfs-1.htm#parcplace_smalltalk" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>].  More
exotic applications of Decorator are the DebuggingGlyph from
InterViews and the PassivityWrapper from ParcPlace Smalltalk. A
DebuggingGlyph prints out debugging information before and after it
forwards a layout request to its component.  This trace information
can be used to analyze and debug the layout behavior of objects in a
complex composition.  The PassivityWrapper can enable or disable user
interactions with the component.</P>

<A NAME="et-use-decor"></A>
<P>But the Decorator pattern is by no means limited to graphical user
interfaces, as the following example (based on the
ET++ streaming classes [<A HREF="bibfs-1.htm#et++" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>]) illustrates.</P>

<A NAME="filestr-183"></A>
<A NAME="stream"></A>
<P>Streams are a fundamental abstraction in most I/O facilities.  A
stream can provide an interface for converting objects into a sequence
of bytes or characters.  That lets us transcribe an object to a file
or to a string in memory for retrieval later. A straightforward way to
do this is to define an abstract Stream class with subclasses
MemoryStream and FileStream.  But suppose we also want to be able to
do the following:</P>

<UL>

<A NAME="lempelziv"></A>
<LI>Compress the stream data using different compression
algorithms (run-length encoding, Lempel-Ziv, etc.).</LI>

<A NAME="ascii7stream"></A>
<LI>Reduce the stream data to 7-bit ASCII characters so that it can be
transmitted over an ASCII communication channel.</LI>

</UL>

<A NAME="auto1067"></A>
<P>The Decorator pattern gives us an elegant way to add these responsibilities
to streams.  The diagram below shows one solution to the problem:</P>

<A NAME="183c"></A>
<P ALIGN=CENTER><IMG SRC="strea010-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/Pictures/strea010.gif"></P>

<A NAME="auto1068"></A>
<P>The Stream abstract class maintains an internal buffer and provides
operations for storing data onto the stream (PutInt, PutString).
Whenever the buffer is full, Stream calls the abstract operation
HandleBufferFull, which does the actual data transfer.  The FileStream
version of this operation overrides this operation to transfer the
buffer to a file.</P>

<A NAME="streamdecorator"></A>
<P>The key class here is StreamDecorator, which maintains a reference to
a component stream and forwards requests to it. StreamDecorator
subclasses override HandleBufferFull and perform additional actions
before calling StreamDecorator's HandleBufferFull operation.</P>

<A NAME="auto1069"></A>
<P>For example, the CompressingStream subclass compresses the data, and
the ASCII7Stream converts the data into 7-bit ASCII.  Now, to create a
FileStream that compresses its data <EM>and</EM> converts the compressed
binary data to 7-bit ASCII, we decorate a FileStream with a
CompressingStream and an ASCII7Stream:</P>

<A NAME="auto1070"></A>
<PRE>
    Stream* aStream = new CompressingStream(
        new ASCII7Stream(
             new FileStream("aFileName")
        )
    );
    aStream->PutInt(12);
    aStream->PutString("aString");
</PRE>

<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next: 
navigation"></A> Related Patterns</H2> 

<A NAME="compadapt"></A>
<P><A HREF="pat4afs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4afs.htm" TARGET="_mainDisplayFrame">Adapter (139)</A>: A
decorator is different from an adapter in that a decorator only
changes an object's responsibilities, not its interface; an adapter
will give an object a completely new interface.</P>

<A NAME="auto1071"></A>
<P><A HREF="pat4cfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>:
A decorator can be viewed as a degenerate composite with only one
component.  However, a decorator adds additional responsibilities&#151;it
isn't intended for object aggregation.</P>

<A NAME="auto1072"></A>
<P><A HREF="pat5ifs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy (315)</A>: A
decorator lets you change the skin of an object; a strategy lets
you change the guts.  These are two alternative ways of changing
an object.</P>

<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat4efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4efs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/rightar3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="pat4efs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4efs.htm"
	TARGET="_mainDisplayFrame">Facade</A><BR>
<A HREF="pat4cfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4cfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/leftarr3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="pat4cfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat4cfs.htm"
	TARGET="_mainDisplayFrame">Composite</A>
</P>

</BODY>

</HTML>

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

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

⌨️ 快捷键说明

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