6
$\begingroup$

I have been working through "C++ Design Patterns and Derivatives Pricing" by Mark Joshi. In chapter 4 the Parameters class uses a bridge pattern and employs a clone() method.

The justification of the pattern mentions that it allows the extension of the parameters class by inheriting from ParametersInner and providing that class with virtual clone(), Integral(double time1, double time2), and IntegralSquare(double time1, double time2) methods.

I can however acheive the same with simple inherritance. I can also avoid having to use the clone method by using std::make_unique outside of the class.

My version of this is as follows. (I use a curve class instead of a parameters class)

#include <vector>
#include <memory>

class curve
{
public:

    virtual double Integral(double time1, double time2) const = 0;
    virtual double IntegralSquare(double time1, double time2) const = 0;

    virtual double Mean(double time1, double time2) const {
        return Integral(time1, time2) / (time2 - time1);
    }

    virtual double RootMeanSquare(double time1, double time2) const {
        return IntegralSquare(time1, time2) / (time2 - time1);
    }
};

class constant_curve : curve
{
public:

    constant_curve() = default;
    constant_curve(const constant_curve &) = default;
    constant_curve & operator=(const constant_curve &) = default;

    constant_curve(double value):
        m_val{value}
    {}

    virtual double Integral(double time1, double time2) const override{
        return (time2 - time1)*m_val;
    }

    virtual double IntegralSquare(double time1, double time2) const  override{
        return (time2 - time1)*m_val*m_val;
    }

private:
    double m_val;
};

int main()
{
    std::vector<std::unique_ptr<curve>> curves;
    curves.emplace_back(std::make_unique<constant_curve>(10));
}

My Question is this:

What are the reasons to use the bridge pattern as used in the book as apposed to the more simple pattern above?

I am not sure if I am allowed to copy the code to this forum, so here is a link to the code. Design Patterns and Derivatives Pricing 2nd Edition.

$\endgroup$

2 Answers 2

4
$\begingroup$

Here are my thought, it is more an attempt to answer than a proper answer, I may be wrong :

Page 54 of the book it is written :

[...] What we would really like is a pay-off class that has the nice polymorphic features that our class does, whilst taking care of its own memory management. There are a couple of approaches to solving this problem. One is to use a wrapper class that has Bridging with a virtual constructor been templatized: this is really generic programming rather than object-oriented programming, and we explore this approach in Section 5.4. The other is known as the bridge pattern.

So I understand that the bridge patterns aims to take care of memory . In the code, the only member of the parameter class is a pointer to the abstract class ParametersInner. And the role of the parameter class is to manage memory associated to the ParametersInner by encapsulating the new (trough the clone) and delete inside its constructor/destructor.

So I believe that this extra level of inheritance acts exactly has a smart pointer does.

The second approach, the wrapper class presented in 5.4 is in essence a smart pointer, but I believe that the use of smart pointer wasn't really common at the time the author wrote the book (2004 for the first edition, and 2007 for the second) and this is probably the reason why he mainly considers the bridge patterns that seems a bit outdated to me. Note that std::make_unique is a c++14 feature that has only been officially released in 2014 and std::make_shared is a feature of c++11 introduced in 2011 .

A short chapter has been added in the second edition to present smart pointer. The author briefly present smart pointer in chapter 13.3, but it seems more like a last minute addition to an already written book (first edition) than a real consideration. As you said, I believe that most of the code's book could have been redesigned in a simplest way thanks to smart pointers.

So to sum up, despite that it is, according to me, an essential book about c++ derivatives, I think it is now a bit outdated, the use of smart pointer, common nowadays, would considerably ease the design.

So finally :

What are the reasons to use the bridge pattern as used in the book as opposed to the more simple pattern above?

Because smart pointers weren't really available at this time (except with the boost library..) , history matters.

$\endgroup$
1
$\begingroup$

Consider that Joshi's Parameters bridge class

class Parameters { // Bridge class
public:
    Parameters(const ParametersInner& innerObject);
    Parameters(const Parameters& original);   // copy constructor
    Parameters& operator=(const Parameters& original);
    virtual ~Parameters();

    // member functions are just wraps from ParametersInner 
    inline double Integral(double time1, double time2) const {
        return InnerObjectPtr->Integral(time1,time2);
    }
    inline double IntegralSquare(double time1, double time2) const {
        return InnerObjectPtr->IntegralSquare(time1,time2);
    }
    double Mean(double time1, double time2) const;
    double RootMeanSquare(double time1, double time2) const;

private:
    ParametersInner* InnerObjectPtr;
};

allows for

  1. memory management for the underlying ParametersInner object
  2. also deep copy of it.

Using std::unique_ptr<ParametersInner> directly in the main code as substitute of the bridge class takes care of memory management, as you already observed.

Albeit having to type std::unique_ptr<ParametersInner> each time can be cumbersome. Additionally, you are not allowed to make copies of unique_ptrs.

You could use shared_ptr<ParameterInner> to fix that, but

  1. shared_ptrs use reference counting which slows down the application, and
  2. shared_ptrs make only shallow copies of their underlying ParameterInner object. That is, the newly copied shared_ptr will point to the same underlying ParameterInner object of the original shared_ptr.

A good compromise here, if we want to preserve the advantages of the Bridge pattern while also adopting modern smart_ptr C++ features, would be to update the Parameters Bridge class to keep a unique_ptr data member:

class Parameters {                              // updated Bridge class
public:
    ...                                              // as before
    virtual ~Parameters(){}                          // modified
    Parameters::Parameters(Parameters&&);            // new
    Parameters& Parameters::operator=(Parameters&&); // new

private:
    std::unique_ptr<ParametersInner> InnerObjectPtr;
};

Update boilerplate member implementations of memory management as below:

Parameters::Parameters(const ParametersInner& innerObject) {
    InnerObjectPtr = std::make_unique<ParametersInner>(innerObject);
}

Parameters::Parameters(const Parameters& original) {
    InnerObjectPtr = std::make_unique<ParametersInner>(original.InnerObjectPtr.get());
}

Parameters& Parameters::operator=(const Parameters& original) {
    if (this != &original) {   
        InnerObjectPtr.reset();
        InnerObjectPtr = std::make_unique<ParametersInner>(original.InnerObjectPtr.get());
    }
    return *this;
}

Parameters::Parameters(Parameters&& original) {
    InnerObjectPtr = std::move(original.InnerObjectPtr);
    // original.InnerObjectPtr is now a nullptr
}

Parameters& Parameters::operator=(Parameters&& original) {
    if (this != &original) {   
        InnerObjectPtr.reset();
        InnerObjectPtr = std::move(original.InnerObjectPtr);
        // original.InnerObjectPtr is now a nullptr
    }
    return *this;
}

This way you can still separate interface from memory management boilerplate for your InnerParameters object (Bridge pattern), while also taking advantage of RAII smart pointers of modern C++.

Also, the smart pointer renders the clone() method in InnerParameters redundant. It can thus be eliminated, and as a consequence that class is now simplified.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.