通过组合添加类功能

发布于 2024-09-14 14:36:14 字数 2255 浏览 3 评论 0原文

假设我们有一个抽象类 ElementTriangleQuadriside 类是从该抽象类派生出来的。

假设这些类与依赖于元素形状的插值方法结合使用。因此,基本上我们创建了一个抽象类 InterpolationElement,从中派生出 InterpolationTriangleInterpolationQuadriterior

然后,为了在 TriangleQuadriside 类中包含插值功能,我们在 Element 类型的类 Element 中添加一个常量引用数据成员>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< /code> 可以这样构造:

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

这是我的问题:为了在我的类 Element 上合并插值方法,这种方法是否正确?这是在专业场景中使用的吗?

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

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

提前致谢。


其他详细信息:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浪推晚风 2024-09-21 14:36:14

这些类与插值方法结合使用。为什么这些方法需要位于单例对象中?这里的单例看起来很有问题。

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!

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!

无法言说的痛 2024-09-21 14:36:14

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

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

人│生佛魔见 2024-09-21 14:36:14

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

所以首先,如果您希望这样做,那么您应该这样做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();
  }
};

这里有 2 个优点:

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

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

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

和策略思路有点不一样,依靠双重调度才能正常工作。但是,它允许您一次性调整 Element 的层次结构(使用 accept 方法),然后添加任意数量的操作。那太好了。

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.

疯了 2024-09-21 14:36:14

您总是可能会弄乱模板。
首先我们有一个顶级班级。

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

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();
}
浪漫之都 2024-09-21 14:36:14

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

根据我对你的问题的理解,这种模式正是为了解决这个问题而设计的。

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文