?? pat4f.htm
字號:
</LI><A NAME="flywt-mng-shar"></A><LI><EM>Managing shared objects.</EM>Because objects are shared, clients shouldn't instantiate themdirectly. FlyweightFactory lets clients locate a particularflyweight. FlyweightFactory objects often use an associative store tolet clients look up flyweights of interest. For example, theflyweight factory in the document editor example can keep a table offlyweights indexed by character codes. The manager returns the properflyweight given its code, creating the flyweight if it does notalready exist.<A NAME="auto1056"></A><P>Sharability also implies some form of reference counting or garbagecollection to reclaim a flyweight's storage when it's no longerneeded. However, neither is necessary if the number of flyweights isfixed and small (e.g., flyweights for the ASCII character set). Inthat case, the flyweights are worth keeping around permanently.</P></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="auto1057"></A><P>Returning to our document formatter example, we can define a<CODE>Glyph</CODE> base class for flyweight graphical objects.Logically, glyphs are Composites (see <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>) that have graphical attributes and candraw themselves. Here we focus on just the font attribute, butthe same approach can be used for any other graphical attributesa glyph might have.</P><A NAME="auto1058"></A><PRE> class Glyph { public: virtual ~Glyph(); virtual void Draw(Window*, GlyphContext&); virtual void SetFont(Font*, GlyphContext&); virtual Font* GetFont(GlyphContext&); virtual void First(GlyphContext&); virtual void Next(GlyphContext&); virtual bool IsDone(GlyphContext&); virtual Glyph* Current(GlyphContext&); virtual void Insert(Glyph*, GlyphContext&); virtual void Remove(GlyphContext&); protected: Glyph(); };</PRE><A NAME="auto1059"></A><P>The <CODE>Character</CODE> subclass just stores a character code:<A NAME="auto1060"></A><PRE> class Character : public Glyph { public: Character(char); virtual void Draw(Window*, GlyphContext&); private: char _charcode; };</PRE><A NAME="auto1061"></A><P>To keep from allocating space for a font attribute in every glyph,we'll store the attribute extrinsically in a <CODE>GlyphContext</CODE>object. <CODE>GlyphContext</CODE> acts as a repository of extrinsicstate. It maintains a compact mapping between a glyph and its font(and any other graphical attributes it might have) in differentcontexts. Any operation that needs to know the glyph's font in agiven context will have a <CODE>GlyphContext</CODE> instance passed to itas a parameter. The operation can then query the<CODE>GlyphContext</CODE> for the font in that context. The contextdepends on the glyph's location in the glyph structure. Therefore<CODE>Glyph</CODE>'s child iteration and manipulation operations mustupdate the <CODE>GlyphContext</CODE> whenever they're used.</P><A NAME="auto1062"></A><PRE> class GlyphContext { public: GlyphContext(); virtual ~GlyphContext(); virtual void Next(int step = 1); virtual void Insert(int quantity = 1); virtual Font* GetFont(); virtual void SetFont(Font*, int span = 1); private: int _index; BTree* _fonts; };</PRE><A NAME="auto1063"></A><P><CODE>GlyphContext</CODE> must be kept informed of the current positionin the glyph structure during traversal. <CODE>GlyphContext::Next</CODE>increments <CODE>_index</CODE> as the traversal proceeds.<CODE>Glyph</CODE> subclasses that have children (e.g., <CODE>Row</CODE> and<CODE>Column</CODE>) must implement <CODE>Next</CODE> so that it calls<CODE>GlyphContext::Next</CODE> at each point in the traversal.</P><A NAME="btree"></A><P><CODE>GlyphContext::GetFont</CODE> uses the index as a key into a<CODE>BTree</CODE> structure that stores the glyph-to-font mapping.Each node in the tree is labeled with the length of the string for whichit gives font information. Leaves in the tree point to a font, whileinterior nodes break the string into substrings, one for each child.</P><A NAME="auto1064"></A><P>Consider the following excerpt from a glyph composition:</P><P ALIGN=CENTER><IMG SRC="Pictures/btree097.gif"></P><A NAME="auto1065"></A><P>The <CODE>BTree</CODE> structure for font information might look like</P><P ALIGN=CENTER><IMG SRC="Pictures/btree-1.gif"></P><A NAME="auto1066"></A><P>Interior nodes define ranges of glyph indices. <CODE>BTree</CODE> isupdated in response to font changes and whenever glyphs are added toor removed from the glyph structure. For example, assuming we're atindex 102 in the traversal, the following code sets the font of eachcharacter in the word "expect" to that of the surrounding text (thatis, <CODE>times12</CODE>, an instance of <CODE>Font</CODE> for 12-pointTimes Roman):</P><A NAME="auto1067"></A><PRE> GlyphContext gc; Font* times12 = new Font("Times-Roman-12"); Font* timesItalic12 = new Font("Times-Italic-12"); // ... gc.SetFont(times12, 6);</PRE><A NAME="auto1068"></A><P>The new <CODE>BTree</CODE> structure (with changes shown in black) lookslike</P><P ALIGN=CENTER><IMG SRC="Pictures/btree-2.gif"></P><A NAME="auto1069"></A><P>Suppose we add the word "don't " (including a trailing space) in12-point Times Italic before "expect." The following code informs the<CODE>gc</CODE> of this event, assuming it is still at index 102:</P><A NAME="auto1070"></A><PRE> gc.Insert(6); gc.SetFont(timesItalic12, 6);</PRE><A NAME="auto1071"></A><P>The <CODE>BTree</CODE> structure becomes</P><P ALIGN=CENTER><IMG SRC="Pictures/btree-3.gif"></P><A NAME="auto1072"></A><P>When the <CODE>GlyphContext</CODE> is queried for the font ofthe current glyph, it descends the <CODE>BTree</CODE>, adding upindices as it goes until it finds the font for the current index.Because the frequency of font changes is relatively low, the treestays small relative to the size of the glyph structure. Thiskeeps storage costs down without an inordinate increase in look-uptime.<A NAME="fn3"></A><A HREF="#footnote3"><SUP>3</SUP></A></P><A NAME="flywt-fact"></A><P>The last object we need is a FlyweightFactory that creates glyphs andensures they're shared properly. Class <CODE>GlyphFactory</CODE>instantiates <CODE>Character</CODE> and other kinds of glyphs. We onlyshare <CODE>Character</CODE> objects; composite glyphs are far lessplentiful, and their important state (i.e., their children) isintrinsic anyway.</P><A NAME="auto1073"></A><PRE> const int NCHARCODES = 128; class GlyphFactory { public: GlyphFactory(); virtual ~GlyphFactory(); virtual Character* CreateCharacter(char); virtual Row* CreateRow(); virtual Column* CreateColumn(); // ... private: Character* _character[NCHARCODES]; };</PRE><A NAME="auto1074"></A><P>The <CODE>_character</CODE> array contains pointers to<CODE>Character</CODE> glyphs indexed by character code. The array isinitialized to zero in the constructor.</P><A NAME="auto1075"></A><PRE> GlyphFactory::GlyphFactory () { for (int i = 0; i < NCHARCODES; ++i) { _character[i] = 0; } }</PRE><A NAME="auto1076"></A><P><CODE>CreateCharacter</CODE> looks up a character in the characterglyph in the array, and it returns the corresponding glyph if itexists. If it doesn't, then <CODE>CreateCharacter</CODE> createsthe glyph, puts it in the array, and returns it:</P><A NAME="auto1077"></A><PRE> Character* GlyphFactory::CreateCharacter (char c) { if (!_character[c]) { _character[c] = new Character(c); } return _character[c]; }</PRE><A NAME="auto1078"></A><P>The other operations simply instantiate a new object each time they'recalled, since noncharacter glyphs won't be shared:</P><A NAME="auto1079"></A><PRE> Row* GlyphFactory::CreateRow () { return new Row; } Column* GlyphFactory::CreateColumn () { return new Column; }</PRE><A NAME="auto1080"></A><P>We could omit these operations and let clients instantiate unsharedglyphs directly. However, if we decide to make these glyphs sharablelater, we'll have to change client code that creates them.</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="auto1081"></A><P>The concept of flyweight objects was first described and explored as adesign technique in InterViews 3.0 [<A HREF="bibfs.htm#interviews_glyphs" TARGET="_mainDisplayFrame">CL90</A>]. Itsdevelopers built a powerful document editor called Doc as a proof ofconcept [<A HREF="bibfs.htm#calder_doc" TARGET="_mainDisplayFrame">CL92</A>]. Doc uses glyph objects to represent eachcharacter in the document. The editor builds one Glyph instance foreach character in a particular style (which defines its graphicalattributes); hence a character's intrinsic state consists of thecharacter code and its style information (an index into a styletable).<A NAME="fn4"></A><A HREF="#footnote4"><SUP>4</SUP></A>That means only position is extrinsic, making Doc fast. Documents arerepresented by a class Document, which also acts as theFlyweightFactory. Measurements on Doc have shown that sharingflyweight characters is quite effective. In a typical case, adocument containing 180,000 characters required allocation of only 480character objects.</P><A NAME="et-use-flywt"></A><P>ET++ [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>] uses flyweights to support look-and-feelindependence.<A NAME="fn5"></A><A HREF="#footnote5"><SUP>5</SUP></A>The look-and-feel standard affects thelayout of user interface elements (e.g., scroll bars, buttons,menus—known collectively as "widgets") and their decorations(e.g., shadows, beveling). A widget delegates all its layout anddrawing behavior to a separate Layout object. Changing the Layoutobject changes the look and feel, even at run-time.</P><A NAME="auto1082"></A><P>For each widget class there is a corresponding Layout class (e.g.,ScrollbarLayout, MenubarLayout, etc.). An obvious problem with thisapproach is that using separate layout objects doubles the number ofuser interface objects: For each user interface object there is anadditional Layout object. To avoid this overhead, Layout objects areimplemented as flyweights. They make good flyweights because theydeal mostly with defining behavior, and it's easy to pass them whatlittle extrinsic state they need to lay out or draw an object.</P><A NAME="auto1083"></A><P>The Layout objects are created and managed by Look objects. The Lookclass is an <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> thatretrieves a specific Layout object with operations likeGetButtonLayout, GetMenuBarLayout, and so forth. For eachlook-and-feel standard there is a corresponding Look subclass (e.g.,MotifLook, OpenLook) that supplies the appropriate Layout objects.</P><A NAME="auto1084"></A><P>By the way, Layout objects are essentially strategies (see<A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy (315)</A>). They are an example of a strategyobject implemented as a flyweight.</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="auto1085"></A><P>The Flyweight pattern is often combined with the <A HREF="pat4cfs.htm"TARGET="_mainDisplayFrame">Composite (163)</A> pattern to implement alogically hierarchical structure in terms of a directed-acyclicgraph with shared leaf nodes.</P><A NAME="auto1086"></A><P>It's often best to implement<A HREF="pat5hfs.htm" TARGET="_mainDisplayFrame">State (305)</A> and<A HREF="pat5ifs.htm" TARGET="_mainDisplayFrame">Strategy (315)</A>objects as flyweights.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat4gfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat4gfs.htm" TARGET="_mainDisplayFrame">Proxy</A><BR><A HREF="pat4efs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat4efs.htm" TARGET="_mainDisplayFrame">Facade</A></P><HR><A NAME="footnote3"></A><P><SUP>3</SUP>Look-up time inthis scheme is proportional to the font change frequency. Worst-caseperformance occurs when a font change occurs on every character, butthat's unusual in practice.</P><A NAME="footnote4"></A><P><SUP>4</SUP>In the Sample Code given earlier, style information is madeextrinsic, leaving the character code as the only intrinsic state.</P><A NAME="footnote5"></A><P><SUP>5</SUP>See <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">AbstractFactory (87)</A> for another approach to look-and-feelindependence.</P></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -