Currency Module User’s Guide : Chapter 5 Currency Conversions : Creating Custom Exchange Objects and Groups
Creating Custom Exchange Objects and Groups
Imagine that you have the following problem. You have exchange rates for many currencies, but all of the rates are for converting currencies to and from US Dollars. There are no rates for directly converting currency A to currency B when neither A nor B are US Dollars. However, it’s likely that you have rates for exchanging A and B to and from US Dollars. In such cases, you can employ an indirect conversion strategy for converting A to B, using US Dollars as the base currency.
This section describes how you can create a class called USDollarBaseExchange to implement such an exchange strategy. The class created here can be used by itself or as an implementation for the RWExchange class.
NOTE >> Source code for the classes and programs described throughout “Creating Custom Exchange Objects and Groups” are available in buildspace\examples\currency.
Class USDollarBaseExchange
The class USDollarBaseExchange inherits from RWExchangeImpl<T>, and implements a currency conversion method that converts a source currency to a US Dollar base, then converts the US Dollar base to a target currency.
Here is the code that declares the class:
 
class USDollarBaseExchange : public RWExchangeImpl<T>
{
public:
// Constructors ---------------------------------------------------
 
USDollarBaseExchange();
USDollarBaseExchange( const USDollarBaseExchange<T>& e );
USDollarBaseExchange( const RWCString& source,
const RWCString& target,
double srcToUsdFactor,
double usdToTrgFactor );
 
// Public Member Functions ----------------------------------------
 
// Inherited from RWExchangeImpl
 
// Return a copy of self off the heap. The copy ctor in the
// handle class RWExchange<T> group needs this function
virtual RWExchangeImpl<T>* clone() const;
 
// Performs the exchange.
virtual RWMoney<T> exchange( const RWMoney<T>& m ) const;
 
// Returns the name of this exchange class
virtual RWCString name() const { return "USDollarBaseMethod"; }
// Accessors ----------------------------------------------------
double sourceToUsdFactor() const { return sourceToUsdFactor_; }
double usdToTargetFactor() const { return usdToTargetFactor_; }
// From RWExchangeImpl
//RWCString source() const;
//RWCString target() const;
 
// Mutators -----------------------------------------------------
void setSourceToUsdFactor( double f ) { sourceToUsdFactor_ = f; }
void setUsdToTargetFactor( double f ) { usdToTargetFactor_ = f; }
 
// From RWExchangeImpl
//void setSource( const RWCString& src );
//void setTarget( const RWCString& trg );
 
// Assignment Operator
USDollarBaseExchange<T>& operator=
( const USDollarBaseExchange<T>& );
 
private:
double sourceToUsdFactor_;
double usdToTargetFactor_;
};
First, this class declares a default and copy constructor, and a constructor that provides a source currency mnemonic, a target currency mnemonic, a conversion factor for source to US Dollars, and a conversion factor for US Dollars to target.
Next, it adds definitions for the following inherited virtual functions, and for the assignment operator:
clone() -- Returns a copy of self off the heap.
exchange() -- Performs the conversion.
name() -- Returns the name of this conversion method, in this case USDollarBaseMethod.
operator()
After defining the virtual functions, the class defines sourceToUsDFactor() and usdToTargetFactor(), which return the conversion factors required for this method of conversion. Similarly, it defines functions setSourceToUsDFactor() and setUsdToTarget, which allow the conversion factors to be set.
Class USDollarBaseGroup
Class USDollarBaseGroup inherits from RWExchangeGroupImpl<T>, and implements a currency exchange group that produces USDollarBaseExchange objects, which convert a source currency to a US Dollar base, then convert the US Dollar base to a target currency. Here is the code that declares the class:
 
template< class T >
class USDollarBaseGroup : public RWExchangeGroupImpl<T>
{
public:
USDollarBaseGroup() {;}
 
// Inherited from RWExchangeGroup
virtual RWExchange<T> getExchange( const RWCString& srcMnemonic,
const RWCString& targetMnemonic,
const RWExchangeRateTable& rates ) const;
virtual RWCString name() const { return "USDollarBaseGroup"; }
 
virtual RWExchangeGroupImpl<T>* clone() const
{ return (RWExchangeGroupImpl<T>*)new
USDollarBaseGroup<T>(*this);}
};
First, this class declares the default constructor. Then it adds definitions for the following inherited virtual functions:
getExchange() -- Creates and returns the currency exchange object.
name() -- Returns the name associated with the group, in this case USDollarBaseGroup.
clone() -- Returns a copy of self allocated off the heap.
Using the Custom Classes
The groupexam example demonstrates how to add the classes described in “Class USDollarBaseExchange” and “Class USDollarBaseGroup” to an exchange factory, and how to perform some currency exchanges. Here is an excerpt from the code that creates and populates a currency exchange factory:
 
//set up exchange rate table rateTable
.
.
.
// Construct an exchange factory with the exchange rate table.
 
RWExchangeFactory<Decimal> exchangeFactory( rateTable );
 
// Exchange groups use a handle/body design. The handle class is
// RWExchangeGroup. Instances of this class contain a pointer to
// an implementation class. The static method
// RWEuroGroup<Decimal>::create() creates an RWExchangeGroup
// object with an RWEuroGroup implementation. Function
// appendExchangeGroup appends a Euro exchange group to the
// factory.
 
exchangeFactory.appendExchangeGroup(
RWEuroGroup<Decimal>::create(euroFile) );
 
// Create a USDollarBaseGroup exchange group and append it to the
// exchange factory.
USDollarBaseGroup<Decimal>* grpImpl =
new USDollarBaseGroup<Decimal>;
exchangeFactory.appendExchangeGroup(
RWExchangeGroup<Decimal>(grpImpl) );
// Create a division exchange group and insert into the factory
// in the second position.
exchangeFactory.insertExchangeGroupAt( 1,
RWDivisionGroup<Decimal>::create() );
The currency exchange factory contains four exchange groups, in the following order: RWMultiplicationGroup, RWDivisionGroup, RWEuroGroup, and USDollarBaseGroup. When the exchange factory gets a request for an exchange object, it will ask each exchange group, in order, to create the exchange object. For example, the following line of code requests an exchange object for converting currency ARP to AUD:
 
RWExchange<Decimal> exchanger =
exchangeFactory.getExchange( "ARP", "AUD" );
In this example, the exchange object, exchanger, is used to perform the actual currency conversion. The example defines function doConversion(), which takes a source currency and an exchange object, and returns a RWMoney object target, which contains the conversion. The doConversion() function prints the results as well. Here is its code:
 
RWMoney<Decimal> doConversion( const RWMoney<Decimal>& source,
const RWExchange<Decimal>& exchanger )
{
RWMoney<Decimal> target = round( exchanger.exchange(source),2U );
cout << "\nUsing exchange method: " << exchanger.name() << endl;
cout << source.amount() << " " << source.currency() << " = " <<
target.amount() << " " << target.currency() << endl;
 
return target;
}
Let’s see how all of these pieces are brought together. The following code excerpt creates two RWMoney objects, source and target, requests an exchange object from the factory, and, if the exchange object is valid, uses the doConversion() function to perform a conversion:
 
RWMoney<Decimal> source( Decimal("8976.80"), "ARP" );
RWMoney<Decimal> target;
RWExchange<Decimal> exchanger =
exchangeFactory.getExchange( "ARP", "AUD" );
if ( !exchanger.isValid() )
{
cout << "No exchange object for ARP to AUD" << endl;
}
else
{
target = doConversion( source, exchanger );
}
You can see the complete source code for this example in buildspace\examples\currency\groupexam.cpp.