⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pat4c.htm

📁 设计模式英文版 作者:Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides 四人帮的书。 学设计模式的必读的书籍!经典中的经典
💻 HTM
📖 第 1 页 / 共 2 页
字号:
    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 a
composite.  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 components
uniformly.  We have to revert to testing for different types before
taking 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.  That
creates a new problem: There's no way to implement
<CODE>Component::Add</CODE> without introducing the possibility of it
failing.  You could make it do nothing, but that ignores an important
consideration; that is, an attempt to add something to a leaf probably
indicates a bug.  In that case, the <CODE>Add</CODE> operation produces
garbage.  You could make it delete its argument, but that might not
be 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 an
exception) if the component isn't allowed to have children or if the
argument 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.  If
the component maintains a parent reference, then we could redefine
<CODE>Component::Remove</CODE> to remove itself from its
parent. However, there still isn't a meaningful interpretation for a
corresponding <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 instance
variable in the Component class where the child access and management
operations are declared.  But putting the child pointer in the base
class incurs a space penalty for every leaf, even though a leaf never
has children.  This is worthwhile only if there are relatively few
children 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 the
earlier Graphics example, ordering may reflect front-to-back ordering.
If Composites represent parse trees, then compound statements can be
instances of a Composite whose children must be ordered to reflect the
program.

<A NAME="auto1074"></A>
<P>When child ordering is an issue, you must design child access
and management interfaces carefully to manage the sequence of
children.  The <A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator&nbsp;(257)</A> pattern
can 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, the
Composite class can cache traversal or search information about its
children.  The Composite can cache actual results or just information
that lets it short-circuit the traversal or search.  For example, the
Picture class from the Motivation example could cache the bounding box
of its children.  During drawing or selection, this cached bounding
box lets the Picture avoid drawing or searching when its children
aren't visible in the current window.

<A NAME="auto1077"></A>
<P>Changes to a component will require invalidating the caches of its
parents.  This works best when components know their parents.  So if
you're using caching, you need to define an interface for telling
composites 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 a
Composite responsible for deleting its children when it's destroyed.
An exception to this rule is when Leaf objects are immutable and thus
can 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 their
children, including linked lists, trees, arrays, and hash tables.  The
choice of data structure depends (as always) on efficiency.  In fact,
it isn't even necessary to use a general-purpose data structure at
all.  Sometimes composites have a variable for each child, although this
requires each subclass of Composite to implement its own management
interface.  See <A HREF="pat5cfs.htm" TARGET="_mainDisplayFrame">Interpreter&nbsp;(243)</A> for an example.</LI>

</OL>

<A NAME="samplecode"></A>
<H2><A HREF="#knownuses"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Sample Code</H2> 

<A NAME="auto1082"></A>
<P>Equipment such as computers and stereo components are often organized
into part-whole or containment hierarchies. For example, a chassis can
contain drives and planar boards, a bus can contain cards, and a
cabinet can contain chassis, buses, and so forth. Such structures can
be modeled naturally with the Composite pattern.</P>

<A NAME="auto1083"></A>
<P><CODE>Equipment</CODE> class defines an interface for all equipment in
the 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 the
attributes of a piece of equipment, like its power consumption and
cost. Subclasses implement these operations for specific kinds of
equipment. <CODE>Equipment</CODE> also declares a
<CODE>CreateIterator</CODE> operation that returns an <CODE>Iterator</CODE>
(see <A HREF="chapCfs.htm" TARGET="_mainDisplayFrame">Appendix&nbsp;C</A>)
for accessing its parts.  The
default 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 that
represent 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 equipment
that 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 and
managing subequipment. The operations <CODE>Add</CODE> and
<CODE>Remove</CODE> insert and delete equipment from the list of equipment
stored in the <CODE>_equipment</CODE> member.  The operation
<CODE>CreateIterator</CODE> returns an iterator (specifically, an
instance 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 the
subequipment<SUP><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 (pretty
simple) 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></A> Known Uses</H2> 

<A NAME="auto1097"></A>
<P>Examples of the Composite pattern can be found in almost all
object-oriented systems.  The original View class of Smalltalk
Model/View/Controller&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=krasner_mvc" TARGET="_mainDisplayFrame">KP88</A>] was a Composite, and nearly every user interface
toolkit or framework has followed in its steps, including ET++ (with
its VObjects&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=et++" TARGET="_mainDisplayFrame">WGM88</A>]) and InterViews (Styles&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>],
Graphics&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=interviews_graphic" TARGET="_mainDisplayFrame">VL88</A>], and
Glyphs&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=interviews_glyphs" TARGET="_mainDisplayFrame">CL90</A>]).  It's interesting to note that the
original View of Model/View/Controller had a set of subviews; in other words, View was
both the Component class and the Composite class.  Release 4.0 of
Smalltalk-80 revised Model/View/Controller with a VisualComponent class that has
subclasses View and CompositeView.</P>

<A NAME="rtlsmall-use-comp"></A>
<A NAME="ssa"></A>
<P>The RTL Smalltalk compiler framework&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=RTLSystem92" TARGET="_mainDisplayFrame">JML92</A>] uses the
Composite pattern extensively.  RTLExpression is a Component class for
parse trees. It has subclasses, such as BinaryExpression, that contain
child RTLExpression objects.  These classes define a composite
structure for parse trees.  RegisterTransfer is the Component class
for a program's intermediate Single Static Assignment (SSA) form.
Leaf subclasses of RegisterTransfer define different static
assignments such as</P>

<UL>

<A NAME="auto1098"></A>
<LI>primitive assignments that perform an operation on two registers and
assign 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, which
indicates that the register is assigned before the routine starts.</LI>

</UL>

<A NAME="auto1103"></A>
<P>Another subclass, RegisterTransferSet, is a Composite class
for representing assignments that change several registers at once.</P>

<A NAME="auto1104"></A>
<P>Another example of this pattern occurs in the financial domain, where
a portfolio aggregates individual assets.  You can support complex
aggregations of assets by implementing a portfolio as a Composite that
conforms to the interface of an individual
asset&nbsp;[<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=birrer-egg_swaps" TARGET="_mainDisplayFrame">BE93</A>].</P>

<A NAME="auto1105"></A>
<P>The <A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame">Command&nbsp;(233)</A> pattern describes how Command objects
can 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></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&nbsp;(223)</A>.</P>

<A NAME="compcomposite"></A>
<P><A HREF="pat4dfs.htm" TARGET="_mainDisplayFrame">Decorator&nbsp;(175)</A> is
often used with Composite.  When decorators and composites are used
together, they will usually have a common parent class.  So decorators
will have to support the Component interface with operations like
Add, Remove, and GetChild.</P>

<A NAME="auto1107"></A>
<P><A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight&nbsp;(195)</A>
lets you share components, but they can no longer refer to their
parents.</P>

<A NAME="auto1108"></A>
<P><A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator&nbsp;(257)</A> can
be used to traverse composites.</P>

<A NAME="auto1109"></A>
<P><A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor&nbsp;(331)</A>
localizes operations and behavior that would otherwise be distributed
across 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 delete
the iterator once you're done with it.  The Iterator pattern shows
how to guard against such bugs on
<A HREF="vfs.htm?doc=pat5d.htm&fid=5d&hid=clean-up_proxy_for_iterators" TARGET="_mainDisplayFrame">page&nbsp;266</A>.</P>

</BODY>

</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -