问题
Some posts have previously asked/discussed whether to include private data members in abstract base classes. I want to explore this here on a concrete example in combination with the decorator pattern.
I came across this issue when I was implementing a Monte Carlo library to price financial derivatives. A random normal number generator is an essential part of a Monte Carlo pricing library. You have some options on how to define you normal variables starting from a random uniform number generator e.g. you can use the Box-Muller method or an inverse cumulative normal function to obtain the normal variables from uniform ones.
You want a public interface that allows to get 1 or more random variables random: e.g. getRandomNormal() and getRamdomNormals(). So lets take an abstract base class RandomNormalGenerator. You want to store your random normals in a vector as you want to do some manipulations on it in subclasses. I decided also to implement anti-thetic sampling in which I take the stored random normals in a class derived from the abstract base and reverse the sign of these random normals (principle of antithetic sampling). My header file looked like this
#ifndef RANDOMNORMALGENERATOR_H_
#define RANDOMNORMALGENERATOR_H_
#include "UniformBridge.h"
#include <memory>
class RandomNormalGenerator {
public:
RandomNormalGenerator();
RandomNormalGenerator(const UniformBridge& uniform_bridge);
void SetSeed(long seed);
virtual double getRandomNormal() = 0;
virtual const std::vector<double>& getRandomNormals(size_t dimension);
protected:
UniformBridge m_UBridge; // Need access to m_UBridge in derived classes.
std::vector<double> m_Normals; //Need access to m_Normals in derived classes.
private:
};
class BoxMullerGenerator: public RandomNormalGenerator {
public:
BoxMullerGenerator();
BoxMullerGenerator(const UniformBridge& uniform_bridge);
virtual double getRandomNormal();
protected:
private:
double m_Cached_value;
bool m_Cached_flag;
};
class AntitheticGenerator: public RandomNormalGenerator {
public:
AntitheticGenerator(std::unique_ptr<RandomNormalGenerator> generator);
virtual double getRandomNormal();
// getRandomNormals will generate in first go, non-antithetic sampled samples,
// when called for second time it will generate antithetic samples.
virtual const std::vector<double>& getRandomNormals(size_t dimension);
protected:
private:
AntitheticGenerator(const AntitheticGenerator&);
AntitheticGenerator& operator=(const AntitheticGenerator&);
private:
std::unique_ptr<RandomNormalGenerator> m_RandNormGenerator;
};
#endif /* RANDOMNORMALGENERATOR_H_ */
Note that AntitheticGenerator is using the decorator pattern it inherits from an abstract base class and has a pointer to this abstract base class (a wrapper around a base class object will do as well).
The AntitheticGenerator will take a pointer e.g. to a BoxMuller and generate random normals using the BoxMuller it will use then the random normals (decorate them) stored in m_Normals from the BoxMuller. Concretely it will reverse the sign of these values (if there was a cached vector in BoxMuller) and store them in its own m_Normals (m_Normals in AntitheticGenerator). Here delegating the m_Normals vector to the abstract base class was very usefull, so that no duplications in subclasses were needed. However, the UniformBridge mUBridge (a bridge to the uniform random generator) in a base class was not a good idea. AntitheticGenerator doesn't need its own uniform random generator as it will use the one in the BoxMuller...and in fact the mUbridge in antitheticGenerator is never used.
So corrected code should look like.
#ifndef RANDOMNORMALGENERATOR_H_
#define RANDOMNORMALGENERATOR_H_
#include "UniformBridge.h"
#include <memory>
class RandomNormalGenerator {
public:
RandomNormalGenerator();
void SetSeed(long seed);
virtual double getRandomNormal() = 0;
virtual const std::vector<double>& getRandomNormals(size_t dimension);
protected:
std::vector<double> m_Normals; //Need access to m_Normals in derived classes.
private:
};
class BoxMullerGenerator: public RandomNormalGenerator {
public:
BoxMullerGenerator();
BoxMullerGenerator(const UniformBridge& uniform_bridge);
virtual double getRandomNormal();
protected:
private:
double m_Cached_value;
bool m_Cached_flag;
UniformBridge m_UBridge;
};
class AntitheticGenerator: public RandomNormalGenerator {
public:
AntitheticGenerator(std::unique_ptr<RandomNormalGenerator> generator);
virtual double getRandomNormal();
// getRandomNormals will generate in first go, non-antithetic sampled samples,
// when called for second time it will generate antithetic samples.
virtual const std::vector<double>& getRandomNormals(size_t dimension);
protected:
private:
AntitheticGenerator(const AntitheticGenerator&);
AntitheticGenerator& operator=(const AntitheticGenerator&);
private:
std::unique_ptr<RandomNormalGenerator> m_RandNormGenerator;
};
#endif /* RANDOMNORMALGENERATOR_H_ */
Conclusion using the decorator pattern for generating random normals, it's useful to define storage of normals in the base class, but storage of a uniform random generator should be delegated to subclasses (the decorated class doesn't need its own).
来源:https://stackoverflow.com/questions/21104642/abstract-base-class-should-have-or-not-have-data-members-when-using-decorator-pa