📄 pat4f.htm
字号:
</LI>
<A NAME="flywt-mng-shar"></A>
<LI><EM>Managing shared objects.</EM>
Because objects are shared, clients shouldn't instantiate them
directly. FlyweightFactory lets clients locate a particular
flyweight. FlyweightFactory objects often use an associative store to
let clients look up flyweights of interest. For example, the
flyweight factory in the document editor example can keep a table of
flyweights indexed by character codes. The manager returns the proper
flyweight given its code, creating the flyweight if it does not
already exist.
<A NAME="auto1056"></A>
<P>Sharability also implies some form of reference counting or garbage
collection to reclaim a flyweight's storage when it's no longer
needed. However, neither is necessary if the number of flyweights is
fixed and small (e.g., flyweights for the ASCII character set). In
that 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></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 can
draw themselves. Here we focus on just the font attribute, but
the same approach can be used for any other graphical attributes
a 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 extrinsic
state. It maintains a compact mapping between a glyph and its font
(and any other graphical attributes it might have) in different
contexts. Any operation that needs to know the glyph's font in a
given context will have a <CODE>GlyphContext</CODE> instance passed to it
as a parameter. The operation can then query the
<CODE>GlyphContext</CODE> for the font in that context. The context
depends on the glyph's location in the glyph structure. Therefore
<CODE>Glyph</CODE>'s child iteration and manipulation operations must
update 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 position
in 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 which
it gives font information. Leaves in the tree point to a font, while
interior 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> is
updated in response to font changes and whenever glyphs are added to
or removed from the glyph structure. For example, assuming we're at
index 102 in the traversal, the following code sets the font of each
character in the word "expect" to that of the surrounding text (that
is, <CODE>times12</CODE>, an instance of <CODE>Font</CODE> for 12-point
Times 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) looks
like</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) in
12-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 of
the current glyph, it descends the <CODE>BTree</CODE>, adding up
indices as it goes until it finds the font for the current index.
Because the frequency of font changes is relatively low, the tree
stays small relative to the size of the glyph structure. This
keeps storage costs down without an inordinate increase in look-up
time.<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 and
ensures they're shared properly. Class <CODE>GlyphFactory</CODE>
instantiates <CODE>Character</CODE> and other kinds of glyphs. We only
share <CODE>Character</CODE> objects; composite glyphs are far less
plentiful, and their important state (i.e., their children) is
intrinsic 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 is
initialized 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 character
glyph in the array, and it returns the corresponding glyph if it
exists. If it doesn't, then <CODE>CreateCharacter</CODE> creates
the 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're
called, 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 unshared
glyphs directly. However, if we decide to make these glyphs sharable
later, 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></A> Known Uses</H2>
<A NAME="auto1081"></A>
<P>The concept of flyweight objects was first described and explored as a
design technique in InterViews 3.0 [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=interviews_glyphs" TARGET="_mainDisplayFrame">CL90</A>]. Its
developers built a powerful document editor called Doc as a proof of
concept [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=calder_doc" TARGET="_mainDisplayFrame">CL92</A>]. Doc uses glyph objects to represent each
character in the document. The editor builds one Glyph instance for
each character in a particular style (which defines its graphical
attributes); hence a character's intrinsic state consists of the
character code and its style information (an index into a style
table).<A NAME="fn4"></A><A HREF="#footnote4"><SUP>4</SUP></A>
That means only position is extrinsic, making Doc fast. Documents are
represented by a class Document, which also acts as the
FlyweightFactory. Measurements on Doc have shown that sharing
flyweight characters is quite effective. In a typical case, a
document containing 180,000 characters required allocation of only 480
character objects.</P>
<A NAME="et-use-flywt"></A>
<P>ET++ [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=et++" TARGET="_mainDisplayFrame">WGM88</A>] uses flyweights to support look-and-feel
independence.<A NAME="fn5"></A><A HREF="#footnote5"><SUP>5</SUP></A>
The look-and-feel standard affects the
layout 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 and
drawing behavior to a separate Layout object. Changing the Layout
object 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 this
approach is that using separate layout objects doubles the number of
user interface objects: For each user interface object there is an
additional Layout object. To avoid this overhead, Layout objects are
implemented as flyweights. They make good flyweights because they
deal mostly with defining behavior, and it's easy to pass them what
little 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 Look
class is an <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> that
retrieves a specific Layout object with operations like
GetButtonLayout, GetMenuBarLayout, and so forth. For each
look-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 strategy
object implemented as a flyweight.</P>
<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0></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 a
logically hierarchical structure in terms of a directed-acyclic
graph 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 in
this scheme is proportional to the font change frequency. Worst-case
performance occurs when a font change occurs on every character, but
that's unusual in practice.
<A HREF="#fn3"><IMG SRC="gifsb/up3.gif" BORDER=0></A></P>
<A NAME="footnote4"></A>
<P><SUP>4</SUP>
In the Sample Code given earlier, style information is made
extrinsic, leaving the character code as the only intrinsic state.
<A HREF="#fn4"><IMG SRC="gifsb/up3.gif" BORDER=0></A></P>
<A NAME="footnote5"></A>
<P><SUP>5</SUP>See <A HREF="pat3afs.htm" TARGET="_mainDisplayFrame">Abstract
Factory (87)</A> for another approach to look-and-feel
independence.
<A HREF="#fn5"><IMG SRC="gifsb/up3.gif" BORDER=0></A></P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -