Visitor Implementation 7/1/2016 CS 631: Visitor Implementation 1 Outline • HW1 Analysis – Question 1: Design – Question 2: Implementation – Question 3: Custom Application • Visitor Implementation – Basic Implementation – Overloading and the Catch-All Function – The Acyclic Visitor 7/1/2016 CS 631: Visitor Implementation 2 HW1: Market Simulator Design 7/1/2016 CS 631: Visitor Implementation 3 HW1: Classes • Instrument: represents a simple financial instrument: – Can be created with default parameters and be modified by parsing a string using fromString() method. • Market: a singleton to get current interest rates: – Loaded from a file; – Interpolation is used if the maturity specified in the getRate() method is not in the maturities map. • Valuation: a class to compute the value: – Encapsulates a pointer to an instrument and returns it so that the instrument can be modified; – Uses the market object and the instrument attributes to calculate the present value. • MarketSimulation: a class to run the simulation: – A static run() method takes names of the market and instruments files: • Creates the market from a file, creates a valuation with an instrument, reads instrument line by line; updates an instrument in the valuation; then computes the present value. 7/1/2016 CS 631: Visitor Implementation 4 HW1: Implementation and Custom Application • Implementation – Use a SingletonHolder with policies to hold the Market class; – Implement the classes above; – Ensure the program works correctly. • Custom Application: – Originality – Proper use of design patterns – Robust UML design • Average grades: – Q1: 8.3, Q2: 8.5, Q3: 8.6 7/1/2016 CS 631: Visitor Implementation 5 Basic Implementation: MarketSimulator class MarketSimulator { public: ... void run();{ for (each deal = // get a Deal object) { DealEvaluator eval(deal); for (each trader = // get Trader object) { trader->accept(eval); double val=eval.getValue(); // find the trader (best_trader) for whom the // deal has the maximum value } DealMaker maker(deal); best_trader->accept(maker); }} ... 7/1/2016 CS 631: Visitor Implementation 6 Basic Implementation: Trader class Trader { public: virtual void accept(TraderVisitor&) = 0; ... }; class Hedger : public class Trader { ... virtual void accept(TraderVisitor& v) { v.visitHedger(*this); } } ... 7/1/2016 CS 631: Visitor Implementation 7 Basic Implementation: TraderVisitor class TraderVisitor { public: virtual void visitHedger(Hedger&) = 0; virtual void visitSpeculator(Speculator&) = 0; virtual void visitArbitrageur(Arbitrageur&) = 0; ... }; class DealEvaluator : public class TraderVisitor { virtual void visitHedger(Hedger&) { // evaluate market conditions // evaluate hedging strategy // assign value } ... } 7/1/2016 CS 631: Visitor Implementation 8 Basic Implementation: DealMaker class DealMaker : public class TraderVisitor { virtual void visitSpeculator(Speculator&) { // execute a deal using the speculating // strategy // update the capital data // update all positions } virtual void visitArbitrageur(Arbitrageur&) { // execute a deal using the arbitrage // update the capital data // update all positions } } 7/1/2016 CS 631: Visitor Implementation 9 Overloading and the Catch-All Function // Make a cosmetic change to get rid of redundancy // in function names class TraderVisitor { public: virtual void visit(Hedger&) = 0; virtual void visit(Speculator&) = 0; virtual void visit(Arbitrageur&) = 0; ... }; // It is now simpler to define all accept() // functions void Hedger::accept(TraderVisitor& v) { v.visit(*this); } 7/1/2016 CS 631: Visitor Implementation 10 Cyclic Dependency • Code dependency analysis – For the Trader class definition to compile it needs to know about TraderVisitor, because of accept functions. – For the TraderVisitor class to compile it has to know about all concrete Trader classes (Hedger, ...). – This is called cyclic dependency. • Cyclic Dependency – Well-known maintenance bottlenecks. – Trader needs TraderVisitor, and TraderVisitor needs the whole Trader hierarchy. Hence Trader depends on its subclasses. – This is a cyclic name dependency: • Class definitions depend only on their names to compile. 7/1/2016 CS 631: Visitor Implementation 11 Cyclic Dependency: Forward Declaration // File TraderVisitor.h class Trader; class Hedger; class Speculator; class Arbitrageur; ... class TraderVisitor { public: virtual void visit(Hedger&) = 0; virtual void visit(Speculator&) = 0; virtual void visit(Arbitrageur&) = 0; ... }; 7/1/2016 CS 631: Visitor Implementation 12 Cyclic Dependency: Forward Declaration (cont.) // File Trader.h class TraderVisitor; class Trader { public: virtual void accept(TraderVisitor&) = 0; ... }; ... 7/1/2016 CS 631: Visitor Implementation 13 Cyclic Dependency: Adding a New Trader • Adding a new Trader type (say RiskTaker) leads to the following: – Add a new forward declaration in TraderVisitor.h – Add a new pure overload to TraderVisitor class • virtual void visit(RiskTaker&) = 0; – Implement visit(RiskTaker&) for every concrete visitor (DealEvaluator, DealMaker). – Implement accept in the RiskTaker class. • All Trader and TraderVisitor hierarchies will be recompiled! 7/1/2016 CS 631: Visitor Implementation 14 The Acyclic Visitor • Invented by Robert Martin in 1996. • Leverages dynamic_cast to eliminate cyclicity. • The approach – Define a strawman base class, BaseVisitor. It does not have any content. – The visited hierarchy accepts a reference to BaseVisitor and applied dynamic_cast to detect the a matching visitor. – Once detected, the accept() function jumps from the visited hierarchy to the visitor hierarchy. 7/1/2016 CS 631: Visitor Implementation 15 The Acyclic Visitor: TraderVisitor class TraderVisitor { public: // The do-nothing destructor ensures: // (1) RTTI capabilities to apply dynamic_cast // (2) Proper polymorphic destruction virtual ~TraderVisitor() {} }; // Define visitor for each concrete Trader class HedgerVisitor { virtual void visit(Hedger&) = 0; }; class SpeculatorVisitor ... class ArbitrageurVisitor ... 7/1/2016 CS 631: Visitor Implementation 16 The Acyclic Visitor: Trader void Hedger::accept(TraderVisitor& v) { if (HedgerVisitor* p = dynamic_cast<HedgerVisitor*>(&v)) { p->visit(*this); } else { ... optionally call a catch-all function ... } } 7/1/2016 CS 631: Visitor Implementation 17 The Acyclic Visitor: DealEvaluator class DealEvaluator : public class TraderVisitor, public class HedgerVisitor, public class SpeculatorVisitor, public class ArbitrageurVisitor { public: void visit(Hedger& h) { ... } void visit(Speculator& s) ... void visit(Arbitrageur& a) ... }; 7/1/2016 CS 631: Visitor Implementation 18 The Acyclic Visitor: Order of Events • Accume the following calls: – – – – DealEvaluator eval; Hedger* trader(...); trader->accept(eval); Order of events • The eval object is converted to TraderVisitor. • The virtual function Hedger::accept is called • Hedger::accept attempts dynamic_cast<HedgerVisitor*> and succeeds, because of multiple inheritance. • Hedger::accept calls visit(Hedger&) • The call reaches DealEvaluator::visit(Hedger&) 7/1/2016 CS 631: Visitor Implementation 19 The Acyclic Visitor: Dependencies • Trader class definition depends on TraderVisitor by name. • HedgerVisitor, and others depend on concrete classes of Trader respectively. • The Hedger::accept implementation fully depends on HedgerVisitor. • Any concrete Visitor definition (DealEvaluator, DealMaker) depends on TraderVisitor and all base visitors. 7/1/2016 CS 631: Visitor Implementation 20