Containment and Aggregation vs. Inheritance

In normal C++ programming, you frequently use inheritance to factor out common behavior into a reusable base class. The CPersistentFrame class (discussed in Chapter 15) is an example of reusability through inheritance.

COM uses containment and aggregation instead of inheritance. Let's start with containment. Suppose you extended the spaceship simulation to include planets in addition to spaceships. Using C++ by itself, you would probably write a COrbiter base class that encapsulated the laws of planetary motion. With COM, you would have "outer" CSpaceship and CPlanet classes plus an "inner" COrbiter class. The outer classes would implement the IVisual interface directly, but those outer classes would delegate their IMotion interfaces to the inner class. The result would look something like this.

click to view at full size.

Note that the COrbiter object doesn't know that it's inside a CSpaceship or CPlanet object, but the outer object certainly knows that it has a COrbiter object embedded inside. The outer class needs to implement all its interface functions, but the IMotion functions, including QueryInterface, simply call the same IMotion functions of the inner class.

A more complex alternative to containment is aggregation. With aggregation, the client can have direct access to the inner object's interfaces. Shown here is the aggregation version of the space simulation.

click to view at full size.

The orbiter is embedded in the spaceship and planet, just as it was in the containment case. Suppose the client obtains an IVisual pointer for a spaceship and then calls QueryInterface for an IMotion pointer. Using the outer IUnknown pointer will draw a blank because the CSpaceship class doesn't support IMotion. The CSpaceship class keeps track of the inner IUnknown pointer (of its embedded COrbiter object), so the class uses that pointer to obtain the IMotion pointer for the COrbiter object.

Now suppose the client obtains an IMotion pointer and then calls QueryInterface for IVisual. The inner object must be able to navigate to the outer object, but how? Take a close look at the CreateInstance call back in Figure 24-10. The first parameter is set to NULL in that case. If you are creating an aggregated (inner) object, you use that parameter to pass an IUnknown pointer for the outer object that you have already created. This pointer is called the controlling unknown. The COrbiter class saves this pointer in a data member and then uses it to call QueryInterface for interfaces that the class itself doesn't support.

The MFC library supports aggregation. The CCmdTarget class has a public data member m_pOuterUnknown that holds the outer object's IUnknown pointer (if the object is aggregated). The CCmdTarget member functions ExternalQueryInterface, ExternalAddRef, and ExternalRelease delegate to the outer IUnknown if it exists. Member functions InternalQueryInterface, InternalAddRef, and InternalRelease do not delegate. See Technical Note #38 in the online documentation for a description of the MFC macros that support aggregation.



Programming Visual C++
Advanced 3ds max 5 Modeling & Animating
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 331
Authors: Boris Kulagin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net