📄 pat5i.htm
字号:
detail.</LI></OL><A NAME="implementation"></A><H2><A HREF="#samplecode"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Sample Code"></A> Implementation</H2> <A NAME="auto1056"></A><P>Consider the following implementation issues:</P><OL><A NAME="auto1057"></A><LI><EM>Defining the Strategy and Context interfaces.</EM>The Strategy and Context interfaces must give a ConcreteStrategyefficient access to any data it needs from a context, and vice versa.<A NAME="auto1058"></A><P>One approach is to have Context pass data in parameters to Strategyoperations—in other words, take the data to the strategy. This keepsStrategy and Context decoupled. On the other hand, Context mightpass data the Strategy doesn't need.</P><A NAME="auto1059"></A><P>Another technique has a context pass <EM>itself</EM> as an argument, andthe strategy requests data from the context explicitly.Alternatively, the strategy can store a reference to its context,eliminating the need to pass anything at all. Either way, thestrategy can request exactly what it needs. But now Context mustdefine a more elaborate interface to its data, which couples Strategyand Context more closely.</P><A NAME="auto1060"></A><P>The needs of the particular algorithm and its data requirements willdetermine the best technique.</P></LI><A NAME="template-impl-strat"></A><LI><EM>Strategies as template parameters.</EM>In C++ templates can be used to configure a class with a strategy.This technique is only applicable if (1) the Strategy can be selectedat compile-time, and (2) it does not have to be changed at run-time.In this case, the class to be configured (e.g., <CODE>Context</CODE>) isdefined as a template class that has a <CODE>Strategy</CODE> class as aparameter:<A NAME="auto1061"></A><PRE> template <class AStrategy> class Context { void Operation() { theStrategy.DoAlgorithm(); } // ... private: AStrategy theStrategy; };</PRE>The class is then configured with a <CODE>Strategy</CODE> class when it'sinstantiated:<A NAME="auto1062"></A><PRE> class MyStrategy { public: void DoAlgorithm(); }; Context<MyStrategy> aContext;</PRE>With templates, there's no need to define an abstract class that definesthe interface to the <CODE>Strategy.</CODE> Using <CODE>Strategy</CODE> as atemplate parameter also lets you bind a <CODE>Strategy</CODE> to its<CODE>Context</CODE> statically, which can increase efficiency.</P></LI><A NAME="strat-optional"></A><LI><EM>Making Strategy objects optional.</EM>The Context class may be simplified if it's meaningful <EM>not</EM> tohave a Strategy object. Context checks to see if it has a Strategyobject before accessing it. If there is one, then Context uses itnormally. If there isn't a strategy, then Context carries out defaultbehavior. The benefit of this approach is that clients don't have todeal with Strategy objects at all <EM>unless</EM> they don't like thedefault behavior.</LI></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="auto1064"></A><P>We'll give the high-level code for the Motivation example, which isbased on the implementation of Composition and Compositor classes inInterViews [<A HREF="bibfs.htm#InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>].</P><A NAME="shrinkability"></A><A NAME="stretchability"></A><P>The <CODE>Composition</CODE> class maintains a collection of<CODE>Component</CODE> instances, which represent text and graphicalelements in a document. A composition arranges component objects intolines using an instance of a <CODE>Compositor</CODE> subclass, whichencapsulates a linebreaking strategy. Each component has anassociated natural size, stretchability, and shrinkability. Thestretchability defines how much the component can grow beyond itsnatural size; shrinkability is how much it can shrink. Thecomposition passes these values to a compositor, which uses them todetermine the best location for linebreaks.</P><A NAME="auto1065"></A><PRE> class Composition { public: Composition(Compositor*); void Repair(); private: Compositor* _compositor; Component* _components; // the list of components int _componentCount; // the number of components int _lineWidth; // the Composition's line width int* _lineBreaks; // the position of linebreaks // in components int _lineCount; // the number of lines };</PRE><A NAME="auto1066"></A><P>When a new layout is required, the composition asks its compositor todetermine where to place linebreaks. The composition passes thecompositor three arrays that define natural sizes, stretchabilities,and shrinkabilities of the components. It also passes the number ofcomponents, how wide the line is, and an array that the compositorfills with the position of each linebreak. The compositor returns thenumber of calculated breaks.</P><A NAME="auto1067"></A><P>The <CODE>Compositor</CODE> interface lets the composition pass thecompositor all the information it needs. This is an example of"taking the data to the strategy":</P><A NAME="auto1068"></A><PRE> class Compositor { public: virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ) = 0; protected: Compositor(); };</PRE><A NAME="auto1069"></A><P>Note that <CODE>Compositor</CODE> is an abstract class. Concretesubclasses define specific linebreaking strategies.</P><A NAME="auto1070"></A><P>The composition calls its compositor in its <CODE>Repair</CODE>operation. <CODE>Repair</CODE> first initializes arrays with the naturalsize, stretchability, and shrinkability of each component (the detailsof which we omit for brevity). Then it calls on the compositor toobtain the linebreaks and finally lays out the components according tothe breaks (also omitted):</P><A NAME="auto1071"></A><PRE> void Composition::Repair () { Coord* natural; Coord* stretchability; Coord* shrinkability; int componentCount; int* breaks; // prepare the arrays with the desired component sizes // ... // determine where the breaks are: int breakCount; breakCount = _compositor->Compose( natural, stretchability, shrinkability, componentCount, _lineWidth, breaks ); // lay out components according to breaks // ... }</PRE><A NAME="simplecompositor2"></A><P>Now let's look at the <CODE>Compositor</CODE> subclasses.<CODE>SimpleCompositor</CODE> examines components a line at a time todetermine where breaks should go:</P><A NAME="auto1072"></A><PRE> class SimpleCompositor : public Compositor { public: SimpleCompositor(); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };</PRE><A NAME="doc-color"></A><A NAME="tex-comp2"></A><P><CODE>TeXCompositor</CODE> uses a more global strategy. It examines a<EM>paragraph</EM> at a time, taking into account the components' sizeand stretchability. It also tries to give an even "color" to theparagraph by minimizing the whitespace between components.</P><A NAME="auto1073"></A><PRE> class TeXCompositor : public Compositor { public: TeXCompositor(); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };</PRE><A NAME="breaks"></A><P><CODE>ArrayCompositor</CODE> breaks the components into lines at regularintervals.</P><A NAME="auto1074"></A><PRE> class ArrayCompositor : public Compositor { public: ArrayCompositor(int interval); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };</PRE><A NAME="auto1075"></A><P>These classes don't use all the information passed in<CODE>Compose</CODE>. <CODE>SimpleCompositor</CODE> ignores the stretchabilityof the components, taking only their natural widths into account.<CODE>TeXCompositor</CODE> uses all the information passed to it, whereas<CODE>ArrayCompositor</CODE> ignores everything.</P><A NAME="auto1076"></A><P>To instantiate <CODE>Composition</CODE>, you pass it the compositoryou want to use:</P><A NAME="auto1077"></A><PRE> Composition* quick = new Composition(new SimpleCompositor); Composition* slick = new Composition(new TeXCompositor); Composition* iconic = new Composition(new ArrayCompositor(100));</PRE><A NAME="auto1078"></A><P><CODE>Compositor</CODE>'s interface is carefully designed to support alllayout algorithms that subclasses might implement. You don't want tohave to change this interface with every new subclass, because that willrequire changing existing subclasses. In general, the Strategy andContext interfaces determine how well the pattern achieves its intent.</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="auto1079"></A><P>Both ET++ [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>] and InterViews use strategies to encapsulatedifferent linebreaking algorithms as we've described.</P><A NAME="rtlsmall-use-strat"></A><P>In the RTL System for compiler code optimization [<A HREF="bibfs.htm#RTLSystem92" TARGET="_mainDisplayFrame">JML92</A>],strategies define different register allocation schemes(RegisterAllocator) and instruction set scheduling policies(RISCscheduler, CISCscheduler). This provides flexibility in targeting theoptimizer for different machine architectures.</P><A NAME="et-swapman"></A><A NAME="future-cashflow"></A><A NAME="swaps"></A><P>The ET++SwapsManager calculation engine framework computes prices fordifferent financial instruments [<A HREF="bibfs.htm#egg-gamma_swaps" TARGET="_mainDisplayFrame">EG92</A>]. Its keyabstractions are Instrument and YieldCurve. Different instruments areimplemented as subclasses of Instrument. YieldCurve calculatesdiscount factors, which determine the present value of future cashflows. Both of these classes delegate some behavior to Strategyobjects. The framework provides a family of ConcreteStrategy classesfor generating cash flows, valuing swaps, and calculating discountfactors. You can create new calculation engines by configuringInstrument and YieldCurve with the different ConcreteStrategy objects.This approach supports mixing and matching existing Strategyimplementations as well as defining new ones.</P><A NAME="template-impl-strat2"></A><P>The Booch components [<A HREF="bibfs.htm#booch_components" TARGET="_mainDisplayFrame">BV90</A>] use strategies as templatearguments. The Booch collection classes support three different kinds ofmemory allocation strategies: managed (allocation out of a pool),controlled (allocations/deallocations are protected by locks), andunmanaged (the normal memory allocator). These strategies are passed astemplate arguments to a collection class when it's instantiated. Forexample, an UnboundedCollection that uses the unmanaged strategy isinstantiated as <CODE>UnboundedCollection<MyItemType*, Unmanaged></CODE>.</P><A NAME="rapp-use-strategy"></A><P>RApp is a system for integrated circuit layout [<A HREF="bibfs.htm#rapp89" TARGET="_mainDisplayFrame">GA89</A>, <A HREF="bibfs.htm#rapp90" TARGET="_mainDisplayFrame">AG90</A>].RApp must lay out and route wires that connect subsystems on thecircuit. Routing algorithms in RApp are defined assubclasses of an abstract Router class. Router is a Strategy class.</P><A NAME="auto1080"></A><P>Borland's ObjectWindows [<A HREF="bibfs.htm#objectwindows" TARGET="_mainDisplayFrame">Bor94</A>] uses strategies in dialogsboxes to ensure that the user enters valid data. For example, numbers mighthave to be in a certain range, and a numeric entry field should acceptonly digits. Validating that a string is correct can require atable look-up.</P><A NAME="validator"></A><P>ObjectWindows uses Validator objects to encapsulate validationstrategies. Validators are examples of Strategy objects. Data entryfields delegate the validation strategy to an optional Validatorobject. The client attaches a validator to a field if validation isrequired (an example of an optional strategy). When the dialog isclosed, the entry fields ask their validators to validate the data.The class library provides validators for common cases, such as aRangeValidator for numbers. New client-specific validation strategiescan be defined easily by subclassing the Validator class.</P><A NAME="relatedpatterns"></A><H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: navigation"></A> Related Patterns</H2> <A NAME="auto1081"></A><P><A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight (195)</A>:Strategy objects often make good flyweights.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat5jfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat5jfs.htm" TARGET="_mainDisplayFrame">Template Method</A><BR><A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame">State</A></P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -