views:

297

answers:

4

I have the following existing classes:

class Gaussian {
public:
  virtual Vector get_mean() = 0;
  virtual Matrix get_covariance() = 0;
  virtual double calculate_likelihood(Vector &data) = 0;
};

class Diagonal_Gaussian : public Gaussian {
public:
  virtual Vector get_mean();
  virtual Matrix get_covariance();
  virtual double calculate_likelihood(Vector &data);
private:
  Vector m_mean;
  Vector m_covariance;
};

class FullCov_Gaussian : public Gaussian {
public:
  virtual Vector get_mean();
  virtual Matrix get_covariance();
  virtual double calculate_likelihood(Vector &data);
private:
  Vector m_mean;
  Matrix m_covariance;
};

As you see, the class Gaussian acts as an interface but doesn't have any implementation. This is all working fine.

Now I want to make an class "AdaptedGaussian" where the data vector provided to the calculated_likelihood will be changed before the likelihood is calculated.

Some requirements:

  • The AdaptedGaussian must be a child-class of Gaussian
  • AdaptedGaussian must be able to "wrap" or "be an instance of" every possible Gaussian class
  • AdaptedGaussian must be constructed from an already existing Gaussian Object

The idea I have now is:

class Adapted_Gaussian : public Gaussian {
private:
  Gaussian* m_g;

public:
  virtual Vector get_mean() { return m_g->get_mean(); }
  virtual Matrix get_covariance() { return m_g->get_covariance(); }
  virtual double calculate_likelihood(Vector &data) 
  { 
    //do something with data
    return g->calculate_likelihood(Vector &data); 
  }
}

There are maybe some disadvantages:

  • For every method (and there are more than showed here) a dummy method must be written in the new class
  • If Gaussian is ever extended, and this class would be forgotten, nasty bugs can appear.

Am I doing this in the right way? Or are there better methods to implement this?

Is there maybe a good way to standard delegate every non-implemented method to the same named method of m_g?

+4  A: 

Looks good, I think this is a pretty classic implementation of the Adapter pattern. Just don't forget to declare a virtual destructor for your Gaussian class. As for the disadvantages.

  1. The way Java class library deal with the dummy method problem is to create a dummy class that provides empty implementation for every single method. All classes that do not want to implement every single method can just inherit from this dummy class and selectively override methods that interests them.
  2. If you extend your Gaussian class with few more methods, as long as you declare them as pure virtual method you will get a compiler error in your child class file anyway.
oykuo
A: 

In Java, it's common to have both an interface and an abstract class that implements it to provide default behavior for all methods. (See Joshua Bloch's design of the Collections API in java.util package.) Perhaps that can help you here as well. You'll give clients a choice of using either the interface or the abstract class.

You can also try composition. Pass an instance of an adapted Gaussian to subclasses and defer behavior to it.

duffymo
+1  A: 

This could maybe also be solved by a Strategy Pattern.

It seams to me, that duffymo also was thinking in this direction with "composition". Change the design in that way, that the base class calls some method of an other object it contains. This object contains the coding for calculate_likelihood. Either the whole method can be deferred or only the modifications (in the second case, the default would be to just do nothing).

E.g.: (corrected version)

class Gaussian {
   private:
      Cl_Strategy* m_cl_strategy;

   public:
      Gaussian(Cl_Strategy* cl_strategy) {
         m_cl_strategy = cl_strategy;
      };
      virtual Vector get_mean() = 0;
      virtual Matrix get_covariance() = 0;
      virtual double _calc_likelihood(Vector &data) = 0;
      virtual double calculate_likelihood(Vector &data) {
         m_cl_strategy->do_your_worst(this, data);
         return _calc_likelihood(data);
      };
};

I hope, I got that one right, my C++ is a little bit dusted ...

_calc_likelihood must be implemented by subclasses and calculate_likelihood binds all together.

Of course, this solution adds a little overhead, but in some situations, the overhead might be OK.

Juergen
This idea is not completely working. Every subclass has it's own implementation of calculate_likelihood and would therefor not have the adapted data.Besides that I would like to have this functionality completely outside the current classes. This because this feature will be possible implemented in different ways/methods and used only in a few % of the application runs.
Peter Smit
Thanks for the feedback. I can also hint as much, as I understand the problem of course. Thats the reason I wrote "maybe". The whole context is not so clear from your code snippets. When every subclass has its own implementation of calculate_likelihood, how can than your example work -- since it also makes some restrictions about it??
Juergen
I don't exactly get why my example would not work. Because the calculate_likelihood is virtual in Gaussian (and there not implemented, see the = 0) the calculate_likelihood function of the real object will be executed. The Gaussian* pointer is always pointing to a an instance of a subclass of Gaussian.
Peter Smit
See my comment at the question.
Juergen
+1  A: 

As you point out writing a lot of basic pass-through functions is tedious and adds an implied maintenance overhead. Also, having a pointer member implies extra (albeit simple) lifetime management issues of the owned pointer. Probably the simplest way to address these issues is to make AdaptedGaussian a template, templated on the specific instance of Gaussian to be adapted.

template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
{
    virtual double calculate_likelihood(Vector &data) 
    { 
        // do something with data
        return BaseGaussian::calculate_likelihood(Vector &data); 
    }
};

This does rely on all adapted instances of Gaussian being default constructible, or at least conforming to a common constructor signature.

If you want to construct an AdaptedGaussian from an existing XXXGaussian, then so long as the XXXGaussian is itself copyable you can add a suitable constructor:

template<class BaseGaussian> class AdaptedGaussian : public BaseGaussian
{
public:
    AdaptedGaussian(const BaseGaussian& other) : BaseGaussian(other)
    {
    }
    // ...
};
Charles Bailey
Hmm, how would I construct an AdaptedGaussian when I only have a pointer to an existing Gaussian?
Peter Smit
Perhaps I misunderstood, in this solution an AdaptedGaussian is an instance of the particular type of Gaussian that is adapted. If you wanted an adapted diagonal gaussian you would create an AdaptedGaussian<Diagonal_Gaussian> instead of a plain Diagonal_Gaussian. As an AdaptedGaussian<T> is a T you can use pointers and references to the AdaptedGaussian<T> as pointers and references to T.
Charles Bailey
When you said: 'AdaptedGaussian must be able to "wrap" or "be an instance of" every possible Gaussian class', I assumed that (a) wrapping an existing instance was not a requirement and, therefore (b) your use of a pointer was an implementation detail because you were aiming for a 'wrapping' solution and not a 'instance of' solution. Did I miss some crucial requirements of your problem?
Charles Bailey
I am sorry that I was not completely clear in my question. The requirement is that the class somehow represents an already existing class.I don't know or there exists an method that achieves this without calling it wrapping?
Peter Smit
You said 'wrap' or 'be an instance of', so I chose one :) . Am I allowed to copy the existing class? If not, then this template solution is not suitable for your requirements.
Charles Bailey
Yes, a copy is allowed. However, the copied class must still behave the same like the old class. (So if it was a DiagonalGaussian object, the calculate_likelihood function of DiagonalGaussian must be called)
Peter Smit
In which case, I think with my added constructor that this solution could work. It rather depends on the details of your situation, though. Construction of the correct AdaptedGaussian depends on having a pointer or reference with the correct static type. If you have a Diagonal_Gaussian pointer then you can construct an AdaptedGaussian<Diagonal_Gaussian> from it, if all you have is a Gaussian pointer which happens to point to a Diagonal_Gaussian instance then you have thrown away some static type information and will need a solution that depends on runtime polymorphism such as what you posted.
Charles Bailey