// Purpose. Extension Object design pattern. // // Summary. Discusses how to use aggregation (instead of mixin multiple // inheritance) to add "interfaces" (or methods) to a class at run-time. // This approach overcomes some limitations in the Visitor and Decorator // patterns - but - it requires the use of RTTI, and, it makes client code // more complex. // // Whereas Decorator requires that the core class's interface remain fixed // as successive "wrappers" are applied, Extension Object allows the class's // interface to grow incrementally and dynamically. // // The focus of the Extension Object pattern is to engineer a class to // support additional methods, or services. Clients that want to leverage // an interface extension, query the object first to see if it supports // the extension before attempting to use it. // // The pattern can tempt the designer to rely on this technique to // rationalize reactive coding rather than commit oneself to proactive // design. The paper offered no discussion of access to private and // protected members of Image by Extension objects. #include #include using std::cout; using std::string; class Extension { public: virtual ~Extension() { } void registerOwner( class Image* o ) { owner = o; } protected: Image* owner; }; class MosaicExtension : public Extension { public: void doMosaic(); }; class HeatExtension : public Extension { public: void doThermalSpectrum(); }; class Image { string name; public: Image( string n ) : name(n) { } string getName() { return name; } void display() { cout << "display: " << name << '\n'; } virtual Extension* getExtension( const type_info& ) { return 0; } }; void MosaicExtension::doMosaic() { cout << " doMosaic: " << owner->getName() << '\n'; } void HeatExtension::doThermalSpectrum() { cout << " doThermalSpectrum: " << owner->getName() << '\n'; } class Lsat : public Image { Extension* exten; public: Lsat( string n, Extension* ex ) : Image( n ) { exten = ex; exten->registerOwner( this ); } ~Lsat() { delete exten; } /*virtual*/ Extension* getExtension( const type_info& ti ) { if (typeid(MosaicExtension) == ti) return exten; return Image::getExtension( ti ); } }; class IR : public Image { Extension* exten; public: IR( string n, Extension* ex ) : Image( n ) { exten = ex; exten->registerOwner( this ); } ~IR() { delete exten; } /*virtual*/ Extension* getExtension( const type_info& ti ) { if (typeid(HeatExtension) == ti) return exten; return Image::getExtension( ti ); } }; void main( void ) { Image* images[4] = { new Lsat( "Western hemisphere", new MosaicExtension() ), new IR( "Desert Storm", new MosaicExtension() ), new Lsat( "Amazon rain forest", new HeatExtension() ), new IR( "Gulf of Oman", new HeatExtension() ) }; Extension* ext; MosaicExtension* mosaicExt; HeatExtension* heatExt; for (int i=0; i < 4; i++) { images[i]->display(); if (ext = images[i]->getExtension( typeid(MosaicExtension) )) { if (mosaicExt = dynamic_cast( ext )) mosaicExt->doMosaic(); } else if (ext = images[i]->getExtension( typeid(HeatExtension) )) { if (heatExt = dynamic_cast( ext )) heatExt->doThermalSpectrum(); } } for (i=0; i < 4; i++) delete images[i]; } // display: Western hemisphere // doMosaic: Western hemisphere // display: Desert Storm // display: Amazon rain forest // display: Gulf of Oman // doThermalSpectrum: Gulf of Oman