通过组合添加类功能

时间:2010-08-12 20:58:19

标签: c++ inheritance polymorphism const-reference

假设我们有一个抽象类Element,派生类TriangleQuadrilateral

假设这些类与依赖于元素形状的插值方法一起使用。所以,基本上我们创建了一个抽象类InterpolationElement,我们从中派生InterpolationTriangleInterpolationQuadrilateral

然后,要在TriangleQuadrilateral类中包含插值功能,我们在类型Element的类InterpolationElement中添加一个const-reference数据成员,即:

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

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

然后我们创建一个方法(如Scott Meyers所述,Effective C ++),将类InterpolationTriangle的本地静态对象实例化为

const InterpolationTriangle& getInterpolationTriangle()
{
    static InterpolationTriangle interpolationTriangle;

    return interpolationTriangle;
}

因此,类Triangle可以构造为:

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

这是我的问题:这种方法是否正确,以便在我的班级Element中加入插值方法?这是在专业场景中使用的吗?

我可以直接在类Element上实现所有插值方法(作为纯虚拟),并在派生类TriangleQuadrilateral中覆盖它们。然而,这种方法在我看来很麻烦,因为每次我需要改进或实现新的插值功能时,我都必须在这些类上做到这一点。而且,使用这种方法,类越来越大(许多方法)。

我想听听您的一些提示和意见

提前致谢。


其他详情:

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}
}

5 个答案:

答案 0 :(得分:1)

这些类与插值方法一起使用。为什么这些方法需要在单个对象中?这里的单身人士看起来很成问题。

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 }
}

另外,欢迎来到SO!

答案 1 :(得分:1)

这让我想起了我回答here的问题。关于数据容器和策略分离的相同想法。

答案 2 :(得分:1)

您的提案存在一个小问题:您已向基类添加了插值相关方法,并且您已更改了构造函数...

首先,如果您希望这样做,请按照以下方式进行:

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();
  }
};

这里有两个优点:

  • 不再需要更改每个派生对象的构造函数
  • 策略对象不再是const,它允许它在计算过程中保持状态......就像对正在插入的当前对象的引用一样。

但是,这仍然需要更改Element类及其每个派生类。难道不打扰你;)?

嗯,是时候(一次)调用设计模式:Visitor

与战略理念略有不同,依靠双重派遣才能正常运作。但是,它允许您调整Element的ONCE(使用accept方法)的层次结构,然后根据需要添加任意数量的操作。这很棒。

答案 3 :(得分:1)

你总是可以用模板搞砸一下。 首先,我们有一流的。

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

...但是我们在层次结构中间也有一个类,它实际上是一个模板。模板不能是顶级类,因为具有不同参数的模板是不同的类。我们的想法是,我们将插值类作为元素的类型参数。

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

插值类。请注意,他们不是兄弟姐妹,因为他们不需要。

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;
        }
};

最后是真实的元素和小的主要程序。

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();
}

摘要:

  • 如果需要,您可以轻松切换每个元素的插值类。
  • 没有双重vtable访问(首先是Element的计算,然后是InterpolationElement的intepolate方法),如Matthieu的例子。每个元素在编译时都知道它正在使用哪个插值类。
  • Element_Impl是一个丑陋的部分,但它使我们免于copypasta。您可以通过实现插值方法包装器进一步扩展它
  • http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

一种方法是使用静态方法,并在Element_Impl中定义一个包装器 - 仍然只在一个地方。

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();
}

答案 4 :(得分:1)

我首先想到的是GoF Design Pattern Visitor

根据我对你的问题的理解,这个模式被设想为完全解决这个问题。

每个Visitor对象定义一种插值技术或应用于对象的算法。

因此,Element类不会随着每个新的functionnality而增长。一旦到位,访问者模式就可以丰富功能,而无需触及基类定义。