Object Factory C++ Implementation 7/1/2016 CS 631: Object Factory 1 Outline • Application – Market Simulator – Instrument Reader – The Need for Object Factories • Implementation – – – – 7/1/2016 Classes and Objects Basic Implementation Using Pointers to Functions Generalization CS 631: Object Factory 2 Market Simulator • Market simulator may need to generate multiple trades (instruments), evaluate such trades, and execute them. • Assume instrument definitions are stored in a file. Each instrument will be represented by different format, for example: – Forward contract (type, asset, maturity, delivery price) • 1 GOLD 1 $100 – Bond (type, principal, maturity, coupon, period) • 2 $100 10 0.6 0.5 • The main class will need to create different instrument classes based on description. 7/1/2016 CS 631: Object Factory 3 Classes and Objects // Instrument, Bond are classes; instrument is an object // Classes are created by programmers; objects by the program Instrument* instrument = new Bond(...); // When classes are objects, they can be created at runtime. // If C++ allowed it: Class read(const char* line); // this is not C++! class InstrumentReader { void load(const char* fileName) { for each line in fileName { Class theClass = read(line); Instrument* instrument = new theClass; } } ... }; 7/1/2016 CS 631: Object Factory 4 Basic Implementation // a unique ID for each instrument type namespace InstrumentType { const int FORWARD = 1, BOND = 2 }; void InstrumentReader::load(std::ifstream& inFile) { char s[256]; while(inFile.getline(s, 256)) { stringstream line(s); // read object type int instrumentType; line >> instrumentType; 7/1/2016 CS 631: Object Factory 5 Basic Implementation: Reading Instrument Instrument* pCurrentInstrument; switch (instrumentType) { using namespace InstrumentType; case FORWARD: pCurrentInstrument = new ForwardContract; break; case BOND: pCurrentInstrument = new Bond; break; default: ... handle error – unknown type ... } // read the instrument's contents by invoking a virtual // function pCurrentInstrument->read(line.str()); ... add the object to container, etc. ... } 7/1/2016 CS 631: Object Factory 6 Basic Implementation: Drawbacks • It performs switch based on a type tag, which is exactly what object-oriented programs try to eliminate. • One class (single source file) has knowledge about all Instrument-derived classes. – It must include all headers of all possible instruments. • It is hard to extend. Creating another type of instrument involves: – add a distinct integral constant to InstrumentType; – output that constant when saving the instrument; – add a new label to the switch statement 7/1/2016 CS 631: Object Factory 7 InstrumentFactory Class class InstrumentFactory { public: // Define a callback function pointer. // The function will create a concrete instrument pointer. typedef Instrument* (*CreateInstrumentCallback)(); private: // Define a map that keeps a unique instrument type // associated with a callback function pointer. typedef std::map<int, CreateInstrumentCallback> CallbackMap; public: // It is a Singleton! static InstrumentFactory& getInstance() {...} // Instrument registration: true if successful bool register(int id, CreateInstrumentCallback fn); // The function that "manufactures" an instrument Instrument* create(int instrumentId); private: CallbackMap callbacks;}; 7/1/2016 CS 631: Object Factory 8 Instrument Registration // In Bond.cpp file: ... // Registration code in an anonymous namespace to make the // function invisible from other modules. namespace { // Trivial callback function Instrument* createBond() {return new Bond;} const int BOND = 2; // This will be created as a static variable const bool registered = InstrumentFactory::getInstance().register( BOND, createBond); } // In ForwardContract.cpp file, similar registration ... 7/1/2016 CS 631: Object Factory 9 Implementing InstrumentFactory bool InstrumentFactory::register(int instrumentId, CreateInstrumentCallback createFn) { // Simply add to the map. (The insert method returns a // pair, with the second element being a success flag.) return this->callbacks.insert( CallbackMap::value_type(instrumentId, createFn)).second; } // Create an instrument pointer based on id Instrument* InstrumentFactory::create(int id) { CallbackMap::const_iterator i = this->callbacks.find(id); if (i == this->callbacks.end()) throw std::runtime_error("Unknown Instrument Id"); // invoke the creation function: return (i->second)(); } 7/1/2016 CS 631: Object Factory 10 InstrumentReader Implementation // Loading different instruments from a file void InstrumentReader::load(std::ifstream& inFile) { char s[256]; while(inFile.getline(s, 256)) { stringstream line(s); // read object type: int instrumentType; line >> instrumentType; Instrument* pCurrentInstrument = InstrumentFactory::getInstance().create(instrumentType); ... process the instrument ... } } 7/1/2016 CS 631: Object Factory 11 Generalization: Object Factory Elements • Concrete Product (Bond, ForwardContract) – A factory delivers some product in the form of an object. • Abstract Product (Instrument) – All products inherit from a base class. • Product Type Identifier – Need to have because of the static C++ type system. • Product Creator – The function or functor specialized for creating exactly one type of object. 7/1/2016 CS 631: Object Factory 12 Generic Object Factory template < class AbstractProduct, typename IdentifierType, typename ProductCreator > class Factory { private: typedef std::map<IdentifierType, ProductCreator> AssocMap; AssocMap associations; 7/1/2016 CS 631: Object Factory 13 Generic Object Factory: Implementation public: bool register(const IdentifierType& id, ProductCreator creator) { return this->associations.insert( AssocMap::value_type(id, creator); } AbstractProduct* create(const IdentifierType& id) { typename AssocMap::const_iterator i = this->associations.find(id); if (i != this->associations.end()) return (i->second)(); throw (...); } }; 7/1/2016 CS 631: Object Factory 14 Generic Object Factory: Usage // Loading different instruments from a file void InstrumentReader::load(std::ifstream& inFile) { typedef Instrument* (*CreateInstrumentCallback)(); // Use SingletonHolder to ensure singleton behavior typedef SingletonHolder<Factory< Instrument,int,CreateInstrumentCallback> > InstrumentFactory; char s[256]; while(inFile.getline(s, 256)) { stringstream line(s); int instrumentType; line >> instrumentType; Instrument* pCurrentInstrument = InstrumentFactory::getInstance().create(instrumentType); ... process the instrument ... } } 7/1/2016 CS 631: Object Factory 15