📄 pat4d.htm
字号:
<A NAME="auto1045"></A><P>For example, we can support different border styles by having thecomponent defer border-drawing to a separate Border object. TheBorder object is a Strategy object that encapsulates a border-drawingstrategy. By extending the number of strategies from just one to anopen-ended list, we achieve the same effect as nesting decoratorsrecursively.</P><A NAME="adorner"></A><A NAME="bedrock1"></A><A NAME="macapp-dec"></A><P>In MacApp 3.0 [<A HREF="bibfs.htm#macapp" TARGET="_mainDisplayFrame">App89</A>] and Bedrock [<A HREF="bibfs.htm#bedrock" TARGET="_mainDisplayFrame">Sym93a</A>], for example,graphical components (called "views") maintain a list of "adorner"objects that can attach additional adornments like borders to a viewcomponent. If a view has any adorners attached, then it gives them achance to draw additional embellishments. MacApp and Bedrock mustuse this approach because the View class is heavyweight. It would betoo 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; thatis, the decorators are transparent to the component:</P><A NAME="dec-180o"></A><P ALIGN=CENTER><IMG SRC="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="Pictures/deco-068.gif"></P><A NAME="dec-180"></A><P>The Strategy-based approach might require modifying the component toaccommodate new extensions. On the other hand, a strategy can haveits own specialized interface, whereas a decorator's interface mustconform to the component's. A strategy for rendering a border, forexample, need only define the interface for rendering a border(DrawBorder, GetWidth, etc.), which means that the strategy can belightweight 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 adorningviews. They also use it to augment the event-handling behavior ofobjects. In both systems, a view maintains a list of "behavior"objects that can modify and intercept events. The view gives each ofthe registered behavior objects a chance to handle the event beforenonregistered behaviors, effectively overriding them. You candecorate a view with special keyboard-handling support, for example,by registering a behavior object that intercepts and handles keyevents.</P></OL><A NAME="samplecode"></A><H2><A HREF="#knownuses"><IMG SRC="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 inC++. 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 differentdecorations.</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 isinitialized in the constructor. For each operation in<CODE>VisualComponent</CODE>'s interface, <CODE>Decorator</CODE> defines adefault 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. Forexample, the class <CODE>BorderDecorator</CODE> adds a border to itsenclosing component. <CODE>BorderDecorator</CODE> is a subclass of<CODE>Decorator</CODE> that overrides the <CODE>Draw</CODE> operationto draw the border. <CODE>BorderDecorator</CODE> also defines a private<CODE>DrawBorder</CODE> helper operation that does the drawing. Thesubclass 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 shadowcapabilities to a visual component.</P><A NAME="auto1057"></A><P>Now we can compose instances of these classes to provide differentdecorations. The following code illustrates how we can usedecorators 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 itinto 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 wedecorate 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'spresence. You, as the client, can still keep track of the text view if youhave to interact with it directly, for example, when you need toinvoke operations that aren't part of the <CODE>VisualComponent</CODE>interface. Clients that rely on the component's identity should referto it directly as well.</P><A NAME="knownuses"></A><H2><A HREF="#relatedpatterns"><IMG SRC="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 addgraphical embellishments to widgets. Examples includeInterViews [<A HREF="bibfs.htm#interviews_composition" TARGET="_mainDisplayFrame">LVC98</A>, <A HREF="bibfs.htm#InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>],ET++ [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>], and theObjectWorks\Smalltalk class library [<A HREF="bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>]. Moreexotic applications of Decorator are the DebuggingGlyph fromInterViews and the PassivityWrapper from ParcPlace Smalltalk. ADebuggingGlyph prints out debugging information before and after itforwards a layout request to its component. This trace informationcan be used to analyze and debug the layout behavior of objects in acomplex composition. The PassivityWrapper can enable or disable userinteractions with the component.</P><A NAME="et-use-decor"></A><P>But the Decorator pattern is by no means limited to graphical userinterfaces, as the following example (based on theET++ streaming classes [<A HREF="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. Astream can provide an interface for converting objects into a sequenceof bytes or characters. That lets us transcribe an object to a fileor to a string in memory for retrieval later. A straightforward way todo this is to define an abstract Stream class with subclassesMemoryStream and FileStream. But suppose we also want to be able todo the following:</P><UL><A NAME="lempelziv"></A><LI>Compress the stream data using different compressionalgorithms (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 betransmitted over an ASCII communication channel.</LI></UL><A NAME="auto1067"></A><P>The Decorator pattern gives us an elegant way to add these responsibilitiesto streams. The diagram below shows one solution to the problem:</P><A NAME="183c"></A><P ALIGN=CENTER><IMG SRC="Pictures/strea010.gif"></P><A NAME="auto1068"></A><P>The Stream abstract class maintains an internal buffer and providesoperations for storing data onto the stream (PutInt, PutString).Whenever the buffer is full, Stream calls the abstract operationHandleBufferFull, which does the actual data transfer. The FileStreamversion of this operation overrides this operation to transfer thebuffer to a file.</P><A NAME="streamdecorator"></A><P>The key class here is StreamDecorator, which maintains a reference toa component stream and forwards requests to it. StreamDecoratorsubclasses override HandleBufferFull and perform additional actionsbefore calling StreamDecorator's HandleBufferFull operation.</P><A NAME="auto1069"></A><P>For example, the CompressingStream subclass compresses the data, andthe ASCII7Stream converts the data into 7-bit ASCII. Now, to create aFileStream that compresses its data <EM>and</EM> converts the compressedbinary data to 7-bit ASCII, we decorate a FileStream with aCompressingStream 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="gifsb/down3.gif" BORDER=0 ALT="next: navigation"></A> Related Patterns</H2> <A NAME="compadapt"></A><P><A HREF="pat4afs.htm" TARGET="_mainDisplayFrame">Adapter (139)</A>: Adecorator is different from an adapter in that a decorator onlychanges an object's responsibilities, not its interface; an adapterwill give an object a completely new interface.</P><A NAME="auto1071"></A><P><A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>:A decorator can be viewed as a degenerate composite with only onecomponent. However, a decorator adds additional responsibilities—itisn't intended for object aggregation.</P><A NAME="auto1072"></A><P><A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy (315)</A>: Adecorator lets you change the skin of an object; a strategy letsyou change the guts. These are two alternative ways of changingan object.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat4efs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat4efs.htm" TARGET="_mainDisplayFrame">Facade</A><BR><A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="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 + -