Добавление классов функциональность через композицию

StackOverflow https://stackoverflow.com/questions/3471968

Вопрос

Предположим, у нас есть абстрактный класс Element из каких классов Triangle а также Quadrilateral происходят от.

Предположим, что эти классы используются в сочетании с методами интерполяции, которые зависят от формы элемента. Итак, в основном мы создаем абстрактный класс InterpolationElement из которого мы имеем InterpolationTriangle а также InterpolationQuadrilateral.

Затем, чтобы включить функциональность интерполяции в Triangle а также Quadrilateral Классы, мы добавляем элемент данных Const-Reference в классе Element Тип InterpolationElement, то есть:

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

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

Затем мы создаем метод (как описано Скотт Мейерсом, эффективным C ++), который отличается от местного статического объекта класса InterpolationTriangle так как

const InterpolationTriangle& getInterpolationTriangle()
{
    static InterpolationTriangle interpolationTriangle;

    return interpolationTriangle;
}

Так что класс Triangle может быть построен как:

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

Вот мой вопрос: это правильный подход, чтобы включить методы интерполяции в моем классе Element? Это используется в профессиональных сценариях?

Я мог бы реализовать напрямую все методы интерполяции на классе Element (как чистый виртуальный) и переопределить их в полученных классах Triangle а также Quadrilateral. Отказ Тем не менее, этот подход мне представляется громоздким, так как каждый раз, когда мне нужно улучшить или реализовать новые функции интерполяции, мне пришлось бы сделать это на этих классах. Более того, классы становятся больше и больше (много методов), используя этот подход.

Я хотел бы услышать от вас несколько советов и комментариев

Заранее спасибо.


Дополнительные детали:

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}
}
Это было полезно?

Решение

Классы используются в сочетании с интерполяцией методы. Отказ Почему эти методы должны быть в одиночном объекте? Синглтон здесь выглядит очень проблематично.

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

Также добро пожаловать!

Другие советы

Это напоминает вопрос о том, что я ответил здесь. Отказ Та же идея о разделении контейнеров данных и стратегии.

Есть одна небольшая проблема с вашим предложением: вы добавили метод, связанный с интерполяцией в ваш базовый класс, и вы изменили конструктор ...

Итак, прежде всего, если вы хотите сделать это таким образом, вот как вы должны сделать это:

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 Метод), а затем добавить как можно больше операций, сколько пожелаете. И это здорово.

Вы всегда можете возиться немного с шаблонами. Сначала у нас есть верхний класс.

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 Access (сначала для расчета элементов, а затем для методов InterpolationElements, как в примере Matthieu. Каждый элемент знает во время компиляции, какой класс интерполяции он использует.
  • Element_impl - некрасивый бит, но он экономит нас из copypasta. Вы можете расширить его еще больше, реализуя метод интерполяции.
  • http://en.wikipedia.org/wiki/curety_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();
}

Что сначала приходит на мой разум, это GOF дизайн шаблон посетителя

От того, что я понимаю о вашей проблеме, этот шаблон задумывается, чтобы точно решить эту проблему.

Каждый объект посетителей определяет методику интерполяции или алгоритм для применения к вашему объекту.

Таким образом, класс элемента вообще не растет с каждой новой функциональностью. Оказавшись на месте, узор посетителя позволяет обогатить функциональность, не касаясь определения базового класса.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top