Bridge Pattern and Smart Pointers for Derivatives Pricing Classes
Summary
The discussion compares a bridge-based design from a derivatives-pricing C++ book with a simpler polymorphic class hierarchy managed through smart pointers. The bridge wraps an implementation object, delegates calculations to it, and handles its lifetime. The responses emphasize that it can also preserve deep-copy behavior, which a unique pointer alone does not provide, while shared pointers generally share the same underlying object rather than cloning it.
The suggested modernized design keeps a unique pointer inside the wrapper and implements copying by constructing a separate implementation object, alongside move operations for ownership transfer. This can retain a stable public interface while using RAII for memory management and may remove the need for a clone method, depending on how the implementation is copied. The exchange also gives historical context: the book predates standard smart pointers. It is a design discussion rather than a benchmark, and its code sketches are not a complete evaluation of alternatives or a quantitative pricing method.
Key ideas
- A bridge wrapper can hide implementation details and manage an implementation object's lifetime.
- A unique pointer manages ownership but does not provide value-style deep copying by itself.
- A bridge can implement deep copies while using a unique pointer internally.
- Shared pointers share ownership and ordinarily do not clone the pointed-to object.
- The discussion concerns software design for pricing code, not evidence about pricing performance.
Tags
Full text
# Mark Joshi, C++ Design Patterns and Derivatives Pricing : Bridge Pattern vs More Simple Inheritance
# Mark Joshi, C++ Design Patterns and Derivatives Pricing : Bridge Pattern vs More Simple Inheritance
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.
## Answer by Malick (score 4, accepted)
https://quant.stackexchange.com/a/32660
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.
## Answer by Giogre (score 1)
https://quant.stackexchange.com/a/71513
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
- memory management for the underlying `ParametersInner` object
- 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_ptr`s.
You could use `shared_ptr<ParameterInner>` to fix that, but
- `shared_ptr`s use reference counting which slows down the application, and
- `shared_ptr`s 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.Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.