views:

138

answers:

5

Hello,

Suppose we have an abstract class Element from which classes Triangle and Quadrilateral are derived from.

Suppose yet that these classes are used in conjunction with interpolation methods that depend on the shape of the element. So, basically we create an abstract class InterpolationElement from which we derive InterpolationTriangle and InterpolationQuadrilateral.

Then, to include the interpolation functionality in the Triangle and Quadrilateral classes, we add a const-reference data member in class Element of type InterpolationElement, that is:

class Element
{
public:
    Element(const InterpolationElement& interp);

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

We then create a method (as described by Scott Meyers, Effective C++) that instanciate a local static object of class InterpolationTriangle as

const InterpolationTriangle& getInterpolationTriangle()
{
    static InterpolationTriangle interpolationTriangle;

    return interpolationTriangle;
}

So that class Triangle can be constructed like:

class Triangle : public Element
{
public:
    Triangle() : Element( getInterpolationTriangle() ) {}
};

Here is my question: is this approach correct in order to incorporate interpolation methods on my class Element? Is this used in professional scenarios?

I could implement directly all the interpolation methods on class Element (as pure virtual) and the override them in the derived classes Triangle and Quadrilateral. However, this approach seems to me to be cumbersome, since every time I need to improve or implement new interpolation functionalities I would have to do that on these classes. Moreover, the classes get bigger and bigger (many methods) using this approach.

I would like to hear from you some tips and comments

Thanks in advance.


Additional details:

class InterpolationElement
{
public:
    InterpolationElement();

    virtual double interpolationMethod1(...) = 0;
                      :
    virtual double interpolationMethodN(...) = 0;
}

class InterpolationTriangle : public InterpolationElement
{
public:
    InterpolationTriangle () {}

    virtual double interpolationMethod1(...) { // interpolation for triangle }
                      :
    virtual double interpolationMethodN(...) { // interpolation for triangle }
}

class InterpolationQuadrilateral : public InterpolationElement
{
public:
    InterpolationTriangle () {}

    virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
                      :
    virtual double interpolationMethod1(...) { // interpolation for quadrilateral}
}
+1  A: 

The classes are used in conjunction with interpolation methods. Why do those methods need to be in a singleton object? The singleton here looks very problematic.

class Element
{
public:
    virtual double interpolationMethod1(...) = 0;
                  :
    virtual double interpolationMethodN(...) = 0;

};

class Triangle : public Element
{
public:
    virtual double interpolationMethod1(...) { // interpolation for triangle }
                  :
    virtual double interpolationMethodN(...) { // interpolation for triangle }
}

Also, welcome to SO!

Potatoswatter
Agreed. The singletons will have no access to triangle- or quadrilateral-specific features of the `Element` instances, so how will they perform their functionality?
Oli Charlesworth
The interpolation classes are independent of the Element, Triangle and Quadrilateral. They do not depend on any of their members. However, when using polymorphism, its is necessary that the appropriate interpolation method be called for the object pointed by Element*. Also, I just need only one object of InterpolationTriangle that will serve all the Triangle objects. Why is the Singleton approach not appropriate here?
Allan
@Allan: The extra classes are simply extraneous. See update. Singletons usually indicate global state, which is a Bad Thing, but in your case they are stateless, so they are more like function pointers. But virtual methods can more cleanly give you that functionality. At the machine level, this simply replaces the `interpolation` member with a vtable pointer.
Potatoswatter
+1  A: 

This is reminiscent of a question that I had answered here. The same idea about the separation of data containers and the strategies.

raja kolluru
+1  A: 

There is one little issue with your proposal: you have added an interpolation related method to your base class and you've changed the constructor...

So first of all, if you wish to do it this way, here is how you should do it:

class Element
{
public:

private:
  // similar signature to a `clone` method
  virtual InterpolationElement* interpolation() const = 0;
};

class Triangle
{
public:

private:
  virtual InterpolationTriangle* interpolation() const
  {
    return new InterpolationTriangle();
  }
};

There are 2 advantages here:

  • It's no longer necessary to change the constructor of each of the derived objects
  • The strategy object is no longer const, which allows it to maintain state during the computation... like a reference to the current object being interpolated.

However, this still requires to change the Element class, and each of its derived classes. Doesn't it bother you ;) ?

Well, it's time (for once) to call upon a Design Pattern: Visitor.

It's a little different from the strategy idea, relying on double dispatch to work properly. However it allows you to tweak the hierarchy of Elements ONCE (with an accept method) and then to add as many operations as you wish. And that is great.

Matthieu M.
+1  A: 

You can always mess a little bit with templates. First we have a top class.

class Element {
    public:
        virtual void calculate() const = 0;
};

... but then we also have a class in the middle of the hierarchy which is actually a template. Template can't be the top level class, as templates with different parameters are different classes. The idea is that we give an interpolation class as a type parameter to the element.

template <typename Interpolation>
class Element_Impl : public Element {
    protected:
        Interpolation m_interpolation;
};

And interpolation classes. Notice, they aren't siblings, because they don't need to.

class InterpolationTriangle {
    public:
        double interpolate(double a, double b) const {
            std::cout << "interpolation triangle" << std::endl;
        }
};
class InterpolationQuadrilateral {
    public:
        double interpolate(double a, double b) const {
            std::cout << "interpolation quadrilateral" << std::endl;
        }
};

And finally the real elements and the small main procedure.

class Triangle : public Element_Impl<InterpolationTriangle> {
    public:
        void calculate() const {
            m_interpolation.interpolate(1.0, 2.0);
        }
};
class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
    public:
        void calculate() const {
            m_interpolation.interpolate(2.0, 3.0);
        }
};
int main() {
    const Element &a = Triangle();
    const Element &b = Quadrilateral();
    a.calculate();
    b.calculate();
}

Summary:

  • you can easily switch interpolation class for each element if needed.
  • there aren't double vtable access (first for Element's calculate and then for InterpolationElement's intepolate methods) as in the Matthieu's example. Each element knows at compile time which interpolation class it is using.
  • Element_Impl is an ugly bit, but it saves us from copypasta. You can expand it even further by implementing interpolation method wrappers
  • http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

One way is to use static methods, and defining a wrapper in Element_Impl - still only in one place.

class Element {
    public:
        virtual void calculate() const = 0;
};

template <typename Interpolation>
class Element_Impl : public Element {
    protected:
        void interpolate(double, double) const {
            Interpolation::interpolate(1, 1);
        }
};

class InterpolationTriangle {
    public:
        static double interpolate(double a, double b) {
            std::cout << "interpolation triangle" << std::endl;
        }
};

class InterpolationQuadrilateral {
    public:
        static double interpolate(double a, double b) {
            std::cout << "interpolation quadrilateral" << std::endl;
        }
};

class Triangle : public Element_Impl<InterpolationTriangle> {
    public:
         void calculate() const {
            interpolate(1.0, 2.0);
        }
};

class Quadrilateral : public Element_Impl<InterpolationQuadrilateral> {
    public:
        void calculate() const {
            interpolate(2.0, 3.0);
        }
};

int main() {
    const Element &a = Triangle();
    const Element &b = Quadrilateral();

    a.calculate();
    b.calculate();
}
phadej
+1  A: 

What first comes to my mind is the GoF Design Pattern Visitor

From what I understand of your problem, this pattern is conceived to exactly solve this issue.

Each Visitor object defines an interpolation technique, or an algorithm to apply to your object.

Thus the Element class doesn't grow at all with each new functionnality. Once in place, the Visitor pattern enables to enrich functionnality without touching to the Base class definition.

Stephane Rolland