4

假设我们有一个抽象类Element,其中类TriangleQuadrilateral派生自。

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

Triangle然后,为了在和Quadrilateral类中包含插值功能,我们在Elementtype 类中添加了一个 const-reference 数据成员InterpolationElement,即:

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

5 回答 5

1

这让人想起我在这里回答的一个问题。关于数据容器和策略分离的相同想法。

于 2010-08-12T23:23:47.933 回答
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!

于 2010-08-12T21:14:14.100 回答
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();
}

概括:

  • 如果需要,您可以轻松切换每个元素的插值类。
  • 没有像 Matthieu 的示例那样的双重 vtable 访问(首先用于 Element 的计算,然后用于 InterpolationElement 的 intepolate 方法)。每个元素在编译时都知道它正在使用哪个插值类。
  • Element_Impl 有点难看,但它使我们免于复制粘贴。您可以通过实现插值方法包装器来进一步扩展它
  • 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();
}
于 2010-08-23T13:19:55.590 回答
1

我首先想到的是GoF 设计模式访问者

根据我对您的问题的理解,这种模式旨在完全解决这个问题。

每个访问者对象都定义了一种插值技术或应用于您的对象的算法。

因此,元素类根本不会随着每个新功能而增长。一旦到位,访问者模式就可以在不触及基类定义的情况下丰富功能。

于 2010-08-23T14:03:40.757 回答
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();
  }
};

这里有2个优点:

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

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

好吧,是时候(一次)调用设计模式了:Visitor.

和策略思路有点不同,依​​靠双重调度才能正常工作。但是,它允许您调整Elements ONCE 的层次结构(使用accept方法),然后添加任意数量的操作。这很棒。

于 2010-08-13T06:51:42.323 回答