previous up next Strategy

Intent, Motivation, Applicability, Structure, Participants, Collaborations, Consequences, Implementation, Sample Code, Known Uses, Related Patterns

previous next Sample Code

We'll give the high-level code for the Motivation example, which is based on the implementation of Composition and Compositor classes in InterViews. The Composition class maintains a collection of Component instances, which represent text and graphical elements in a document. A composition arranges component objects into lines using an instance of a Compositor subclass, which encapsulates a linebreaking strategy. Each component has an associated natural size, stretchability, and shrinkability. The stretchability defines how much the component can grow beyond its natural size; shrinkability is how much it can shrink. The composition passes these values to a compositor, which uses them to determine the best location for linebreaks.

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 };
When a new layout is required, the composition asks its compositor to determine where to place linebreaks. The composition passes the compositor three arrays that define natural sizes, stretchabilities, and shrinkabilities of the components. It also passes the number of components, how wide the line is, and an array that the compositor fills with the position of each linebreak. The compositor returns the number of calculated breaks. The Compositor interface lets the composition pass the compositor all the information it needs. This is an example of ``taking the data to the strategy'':
class Compositor { public: virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ) = 0; protected: Compositor(); };
Note that Compositor is an abstract class. Concrete subclasses define specific linebreaking strategies. The composition calls its compositor in its Repair operation. Repair first initializes arrays with the natural size, stretchability, and shrinkability of each component (the details of which we omit for brevity). Then it calls on the compositor to obtain the linebreaks and finally lays out the components according to the breaks (also omitted):
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 // ... }
Now let's look at the Compositor subclasses. SimpleCompositor examines components a line at a time to determine where breaks should go:
class SimpleCompositor : public Compositor { public: SimpleCompositor(); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };
TeXCompositor uses a more global strategy. It examines a paragraph at a time, taking into account the components' size and stretchability. It also tries to give an even ``color'' to the paragraph by minimizing the whitespace between components.
class TeXCompositor : public Compositor { public: TeXCompositor(); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };
ArrayCompositor breaks the components into lines at regular intervals.
class ArrayCompositor : public Compositor { public: ArrayCompositor(int interval); virtual int Compose( Coord natural[], Coord stretch[], Coord shrink[], int componentCount, int lineWidth, int breaks[] ); // ... };
These classes don't use all the information passed in Compose. SimpleCompositor ignores the stretchability of the components, taking only their natural widths into account. TeXCompositor uses all the information passed to it, whereas ArrayCompositor ignores everything. To instantiate Composition, you pass it the compositor you want to use:
Composition* quick = new Composition(new SimpleCompositor); Composition* slick = new Composition(new TeXCompositor); Composition* iconic = new Composition(new ArrayCompositor(100));
Compositor's interface is carefully designed to support all layout algorithms that subclasses might implement. You don't want to have to change this interface with every new subclass, because that will require changing existing subclasses. In general, the Strategy and Context interfaces determine how well the pattern achieves its intent.


Sample Code of Abstract Factory, Adapter, Bridge, Builder, Chain of Responsibility, Command, Composite, Decorator, Facade, Factory Method, Flyweight, Interpreter, Iterator, Mediator, Memento, Observer, Prototype, Proxy, Singleton, State, Strategy, Template Method, Visitor

previous next