I was wondering if this is the best possible design for my situation. Note that this is a simpler version of the problem I am trying to tackle.
I have a class Stage
that holds data as two vectors: weights w_
and values x_
. I'm interested in calculating certain statistics like the weighted mean / variance etc. So I have a method Stage::ReportStatistic
.
The Statistic
class is abstract and goes as a pointer argument to Stage::ReportStatistic
. The Statistic
class has a method Statistic::Value
that takes two vectors and calculates the statistic in any particular implementation, such as my StatisticMean::Value
.
I find that my Statistic::Value
method needs references to private members of the Stage
class. Is there a way to avoid the signature Statistic::Value(std::vector<double> const &, std::vector<double> const &)
? If I changed the representation of data in stage to std::vector<std::pair<double, double> >
instead, it would break Statistic::Value
. Is it asking for too much to come up with a design that could avoid this problem?
The code is complete and can be compiled using something like g++ -std=c++11 <whatever>.cpp
.
#include <vector>
#include <memory>
#include <random>
#include <queue>
#include <stdexcept>
#include <iostream>
// BEGIN statistic.h
namespace project
{
// Abstract class that takes in data and spits out a statistic through its Value() method
class Statistic
{
public:
virtual double Value(std::vector<double> const &, std::vector<double> const &) = 0;
};
}
// END statistic.h
//BEGIN statisticmean.h
namespace project
{
// An implementation of statistic whose Value() method computes the weighted mean of the data
class StatisticMean: public Statistic
{
public:
class StatisticMeanBuilder;
StatisticMean() {}
double Value(std::vector<double> const &x, std::vector<double> const &w);
};
class StatisticMean::StatisticMeanBuilder
{
public:
StatisticMeanBuilder() {}
std::unique_ptr<Statistic> Build()
{
std::unique_ptr<Statistic> x(new StatisticMean());
return x;
}
};
}
// END statisticmean.h
// BEGIN stage.h
namespace project
{
// A data-holder. Method ReportStatistic returns a statistic according to the input.
class Stage
{
private:
std::vector<double> x_;
std::vector<double> w_;
public:
Stage(std::vector<double> const &, std::mt19937_64 &);
double ReportStatistic(std::unique_ptr<Statistic>);
};
}
// END stage.h
// BEGIN statisticmean.cpp
namespace project
{
// Implementation: nothing to see here...
double StatisticMean::Value(std::vector<double> const &x, std::vector<double> const &w)
{
const int N = x.size();
try
{
if(N == 0) throw std::logic_error("Error: Must have at least one x element in StatisticMean");
if(w.size() != N) throw std::logic_error("Error: Vector sizes inconsistent in StatisticMean");
}
catch(std::logic_error &e)
{
std::cerr << e.what() << std::endl;
}
double result = 0.0;
for(int i = 0; i < N; i++)
result += x[i] * w[i];
return result;
}
}
// END statisticmean.cpp
// BEGIN stage.cpp
namespace project
{
// Implementation: nothing to see here...
Stage::Stage(std::vector<double> const &w, std::mt19937_64 &g)
{
int N = w.size();
std::normal_distribution<double> du(0.0, 1.0);
this->x_.resize(N);
this->w_ = w;
for(int i = 0; i < N; i++)
this->x_[i] = du(g);
}
double Stage::ReportStatistic(std::unique_ptr<Statistic> s)
{
return s->Value(this->x_, this->w_);
}
}
// END stage.cpp
// BEGIN main.cpp
int main()
{
// Random number generator
std::random_device rd;
std::mt19937_64 g(rd());
// Tiny vectors for working example
std::vector<double> weights(3);
weights[0] = 0.0; weights[1] = 0.0; weights[2] = 0.0;
// Construct the data-holder
project::Stage x(weights, g);
// Create a list of statistics that you'd like to see
// There's only a mean implementation, but you could imagine others (variance etc)
std::queue<std::unique_ptr<project::Statistic> > s;
s.push(std::move(project::StatisticMean::StatisticMeanBuilder().Build()));
while(!s.empty())
{
// Calculate each statistic according to the queue
std::cout << x.ReportStatistic(std::move(s.front())) << std::endl;
s.pop();
}
return 0;
}
// END main.cpp
A more realistic version of this problem:
Roughly speaking, this class takes probability distribution, samples from it, and then transforms each sample according to some model function. Once the object is constructed in this way, I'd like to report statistics on the transformed samples (say x
here).
Anything pointers going into the methods of Stage
would need to access the private members of Stage
through something like references. If I change the representation of private data in Stage
, it would force me to rewrite even the headers of Model
and Reporter
.
The pros of this design is that I can write thousands of Model
and Reporter
children according to what I want by simple inheritance. And it would work just fine unless, somewhere down the line, I needed to change how the data is represented in Stage
. I'd like to avoid this problem.