?? pat4c.htm
字號:
class Component { public: //... virtual Composite* GetComposite() { return 0; } }; class Composite : public Component { public: void Add(Component*); // ... virtual Composite* GetComposite() { return this; } }; class Leaf : public Component { // ... };</PRE><A NAME="auto1064"></A><P><CODE>GetComposite</CODE> lets you query a component to see if it's acomposite. You can perform <CODE>Add</CODE> and<CODE>Remove</CODE> safely on the composite it returns.</P><A NAME="auto1065"></A><PRE> Composite* aComposite = new Composite; Leaf* aLeaf = new Leaf; Component* aComponent; Composite* test; aComponent = aComposite; if (test = aComponent->GetComposite()) { test->Add(new Leaf); } aComponent = aLeaf; if (test = aComponent->GetComposite()) { test->Add(new Leaf); // will not add leaf }</PRE><A NAME="dynamic_cast"></A><P>Similar tests for a Composite can be done using the C++<CODE>dynamic_cast</CODE> construct.</P><A NAME="auto1066"></A><P>Of course, the problem here is that we don't treat all componentsuniformly. We have to revert to testing for different types beforetaking the appropriate action.</P><A NAME="auto1067"></A><P>The only way to provide transparency is to define default<CODE>Add</CODE> and <CODE>Remove</CODE> operations in Component. Thatcreates a new problem: There's no way to implement<CODE>Component::Add</CODE> without introducing the possibility of itfailing. You could make it do nothing, but that ignores an importantconsideration; that is, an attempt to add something to a leaf probablyindicates a bug. In that case, the <CODE>Add</CODE> operation producesgarbage. You could make it delete its argument, but that might notbe what clients expect.</P><A NAME="auto1068"></A><P>Usually it's better to make <CODE>Add</CODE> and<CODE>Remove</CODE> fail by default (perhaps by raising anexception) if the component isn't allowed to have children or if theargument of <CODE>Remove</CODE> isn't a child of the component,respectively.</P><A NAME="auto1069"></A><P>Another alternative is to change the meaning of "remove" slightly. Ifthe component maintains a parent reference, then we could redefine<CODE>Component::Remove</CODE> to remove itself from itsparent. However, there still isn't a meaningful interpretation for acorresponding <CODE>Add</CODE>.</P></LI><A NAME="auto1070"></A><P></P><A NAME="auto1071"></A><LI><EM>Should Component implement a list of Components?</EM>You might be tempted to define the set of children as an instancevariable in the Component class where the child access and managementoperations are declared. But putting the child pointer in the baseclass incurs a space penalty for every leaf, even though a leaf neverhas children. This is worthwhile only if there are relatively fewchildren in the structure.</LI><A NAME="auto1072"></A><P></P><A NAME="auto1073"></A><LI><EM>Child ordering.</EM>Many designs specify an ordering on the children of Composite. In theearlier Graphics example, ordering may reflect front-to-back ordering.If Composites represent parse trees, then compound statements can beinstances of a Composite whose children must be ordered to reflect theprogram.<A NAME="auto1074"></A><P>When child ordering is an issue, you must design child accessand management interfaces carefully to manage the sequence ofchildren. The <A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A> patterncan guide you in this.</P></LI><A NAME="auto1075"></A><P></P><A NAME="auto1076"></A><LI><EM>Caching to improve performance.</EM>If you need to traverse or search compositions frequently, theComposite class can cache traversal or search information about itschildren. The Composite can cache actual results or just informationthat lets it short-circuit the traversal or search. For example, thePicture class from the Motivation example could cache the bounding boxof its children. During drawing or selection, this cached boundingbox lets the Picture avoid drawing or searching when its childrenaren't visible in the current window.<A NAME="auto1077"></A><P>Changes to a component will require invalidating the caches of itsparents. This works best when components know their parents. So ifyou're using caching, you need to define an interface for tellingcomposites that their caches are invalid.</P></LI><A NAME="auto1078"></A><P></P><A NAME="auto1079"></A><LI><EM>Who should delete components?</EM>In languages without garbage collection, it's usually best to make aComposite responsible for deleting its children when it's destroyed.An exception to this rule is when Leaf objects are immutable and thuscan be shared.</LI><A NAME="auto1080"></A><P></P><A NAME="auto1081"></A><LI><EM>What's the best data structure for storing components?</EM>Composites may use a variety of data structures to store theirchildren, including linked lists, trees, arrays, and hash tables. Thechoice of data structure depends (as always) on efficiency. In fact,it isn't even necessary to use a general-purpose data structure atall. Sometimes composites have a variable for each child, although thisrequires each subclass of Composite to implement its own managementinterface. See <A HREF="pat5cfs.htm" TARGET="_mainDisplayFrame">Interpreter (243)</A> for an example.</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="auto1082"></A><P>Equipment such as computers and stereo components are often organizedinto part-whole or containment hierarchies. For example, a chassis cancontain drives and planar boards, a bus can contain cards, and acabinet can contain chassis, buses, and so forth. Such structures canbe modeled naturally with the Composite pattern.</P><A NAME="auto1083"></A><P><CODE>Equipment</CODE> class defines an interface for all equipment inthe part-whole hierarchy.</P><A NAME="auto1084"></A><PRE> class Equipment { public: virtual ~Equipment(); const char* Name() { return _name; } virtual Watt Power(); virtual Currency NetPrice(); virtual Currency DiscountPrice(); virtual void Add(Equipment*); virtual void Remove(Equipment*); virtual Iterator<Equipment*>* CreateIterator(); protected: Equipment(const char*); private: const char* _name; };</PRE><A NAME="auto1085"></A><P><CODE>Equipment</CODE> declares operations that return theattributes of a piece of equipment, like its power consumption andcost. Subclasses implement these operations for specific kinds ofequipment. <CODE>Equipment</CODE> also declares a<CODE>CreateIterator</CODE> operation that returns an <CODE>Iterator</CODE>(see <A HREF="chapCfs.htm" TARGET="_mainDisplayFrame">Appendix C</A>)for accessing its parts. Thedefault implementation for this operation returns a NullIterator,which iterates over the empty set.</P><A NAME="auto1086"></A><P>Subclasses of <CODE>Equipment</CODE> might include Leaf classes thatrepresent disk drives, integrated circuits, and switches:</P><A NAME="auto1087"></A><PRE> class FloppyDisk : public Equipment { public: FloppyDisk(const char*); virtual ~FloppyDisk(); virtual Watt Power(); virtual Currency NetPrice(); virtual Currency DiscountPrice(); };</PRE><A NAME="auto1088"></A><P><CODE>CompositeEquipment</CODE> is the base class for equipmentthat contains other equipment. It's also a subclass of<CODE>Equipment</CODE>.</P><A NAME="auto1089"></A><PRE> class CompositeEquipment : public Equipment { public: virtual ~CompositeEquipment(); virtual Watt Power(); virtual Currency NetPrice(); virtual Currency DiscountPrice(); virtual void Add(Equipment*); virtual void Remove(Equipment*); virtual Iterator<Equipment*>* CreateIterator(); protected: CompositeEquipment(const char*); private: List<Equipment*> _equipment; };</PRE><A NAME="auto1090"></A><P><CODE>CompositeEquipment</CODE> defines the operations for accessing andmanaging subequipment. The operations <CODE>Add</CODE> and<CODE>Remove</CODE> insert and delete equipment from the list of equipmentstored in the <CODE>_equipment</CODE> member. The operation<CODE>CreateIterator</CODE> returns an iterator (specifically, aninstance of <CODE>ListIterator</CODE>) that will traverse this list.</P><A NAME="auto1091"></A><P>A default implementation of <CODE>NetPrice</CODE> might use<CODE>CreateIterator</CODE> to sum the net prices of thesubequipment<SUP><A NAME="fn2"></A><A HREF="#footnote2">2</A></SUP>:</P><A NAME="auto1092"></A><PRE> Currency CompositeEquipment::NetPrice () { Iterator<Equipment*>* i = CreateIterator(); Currency total = 0; for (i->First(); !i->IsDone(); i->Next()) { total += i->CurrentItem()->NetPrice(); } delete i; return total; }</PRE><A NAME="auto1093"></A><P>Now we can represent a computer chassis as a subclass of<CODE>CompositeEquipment</CODE> called <CODE>Chassis</CODE>.<CODE>Chassis</CODE> inherits the child-related operations from<CODE>CompositeEquipment</CODE>.</P><A NAME="auto1094"></A><PRE> class Chassis : public CompositeEquipment { public: Chassis(const char*); virtual ~Chassis(); virtual Watt Power(); virtual Currency NetPrice(); virtual Currency DiscountPrice(); };</PRE><A NAME="auto1095"></A><P>We can define other equipment containers such as<CODE>Cabinet</CODE> and <CODE>Bus</CODE> in a similar way.That gives us everything we need to assemble equipment into a (prettysimple) personal computer:</P><A NAME="auto1096"></A><PRE> Cabinet* cabinet = new Cabinet("PC Cabinet"); Chassis* chassis = new Chassis("PC Chassis"); cabinet->Add(chassis); Bus* bus = new Bus("MCA Bus"); bus->Add(new Card("16Mbs Token Ring")); chassis->Add(bus); chassis->Add(new FloppyDisk("3.5in Floppy")); cout << "The net price is " << chassis->NetPrice() << endl;</PRE><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="auto1097"></A><P>Examples of the Composite pattern can be found in almost allobject-oriented systems. The original View class of SmalltalkModel/View/Controller [<A HREF="bibfs.htm#krasner_mvc" TARGET="_mainDisplayFrame">KP88</A>] was a Composite, and nearly every user interfacetoolkit or framework has followed in its steps, including ET++ (withits VObjects [<A HREF="bibfs.htm#et++" TARGET="_mainDisplayFrame">WGM88</A>]) and InterViews (Styles [<A HREF="bibfs.htm#InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>],Graphics [<A HREF="bibfs.htm#interviews_graphic" TARGET="_mainDisplayFrame">VL88</A>], andGlyphs [<A HREF="bibfs.htm#interviews_glyphs" TARGET="_mainDisplayFrame">CL90</A>]). It's interesting to note that theoriginal View of Model/View/Controller had a set of subviews; in other words, View wasboth the Component class and the Composite class. Release 4.0 ofSmalltalk-80 revised Model/View/Controller with a VisualComponent class that hassubclasses View and CompositeView.</P><A NAME="rtlsmall-use-comp"></A><A NAME="ssa"></A><P>The RTL Smalltalk compiler framework [<A HREF="bibfs.htm#RTLSystem92" TARGET="_mainDisplayFrame">JML92</A>] uses theComposite pattern extensively. RTLExpression is a Component class forparse trees. It has subclasses, such as BinaryExpression, that containchild RTLExpression objects. These classes define a compositestructure for parse trees. RegisterTransfer is the Component classfor a program's intermediate Single Static Assignment (SSA) form.Leaf subclasses of RegisterTransfer define different staticassignments such as</P><UL><A NAME="auto1098"></A><LI>primitive assignments that perform an operation on two registers andassign the result to a third;</LI><A NAME="auto1099"></A><P></P><A NAME="auto1100"></A><LI>an assignment with a source register but no destination register,which indicates that the register is used after a routine returns; and</LI><A NAME="auto1101"></A><P></P><A NAME="auto1102"></A><LI>an assignment with a destination register but no source, whichindicates that the register is assigned before the routine starts.</LI></UL><A NAME="auto1103"></A><P>Another subclass, RegisterTransferSet, is a Composite classfor representing assignments that change several registers at once.</P><A NAME="auto1104"></A><P>Another example of this pattern occurs in the financial domain, wherea portfolio aggregates individual assets. You can support complexaggregations of assets by implementing a portfolio as a Composite thatconforms to the interface of an individualasset [<A HREF="bibfs.htm#birrer-egg_swaps" TARGET="_mainDisplayFrame">BE93</A>].</P><A NAME="auto1105"></A><P>The <A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command (233)</A> pattern describes how Command objectscan be composed and sequenced with a MacroCommand Composite 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="auto1106"></A><P>Often the component-parent link is used for a <A HREF="pat5afs.htm"TARGET="_mainDisplayFrame">Chain of Responsibility (223)</A>.</P><A NAME="compcomposite"></A><P><A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator (175)</A> isoften used with Composite. When decorators and composites are usedtogether, they will usually have a common parent class. So decoratorswill have to support the Component interface with operations likeAdd, Remove, and GetChild.</P><A NAME="auto1107"></A><P><A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight (195)</A>lets you share components, but they can no longer refer to theirparents.</P><A NAME="auto1108"></A><P><A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A> canbe used to traverse composites.</P><A NAME="auto1109"></A><P><A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor (331)</A>localizes operations and behavior that would otherwise be distributedacross Composite and Leaf classes.</P><A NAME="last"></A><P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR><A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator</A><BR><A HREF="pat4bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif" ALIGN=TOP BORDER=0></A> <A HREF="pat4bfs.htm" TARGET="_mainDisplayFrame">Bridge</A></P><HR><A NAME="footnote2"></A><P><SUP>2</SUP>It's easy to forget to deletethe iterator once you're done with it. The Iterator pattern showshow to guard against such bugs on<A HREF="pat5dfs.htm#clean-up_proxy_for_iterators" TARGET="_mainDisplayFrame">page 266</A>.</P></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -