//
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 <iostream>
#include
<string>
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<MosaicExtension*>( ext ))
mosaicExt->doMosaic();
} else if (ext =
images[i]->getExtension( typeid(HeatExtension) )) {
if (heatExt =
dynamic_cast<HeatExtension*>( 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