سؤال

لنفترض أن لدينا فئة مجردة Element من أي فصول Triangle و Quadrilateral مشتقة من.

لنفترض حتى الآن أن هذه الفئات تستخدم بالاقتران مع طرق الاستيفاء التي تعتمد على شكل العنصر. لذلك ، في الأساس ننشئ فئة مجردة InterpolationElement التي نستمد منها InterpolationTriangle و InterpolationQuadrilateral.

ثم ، لتضمين وظيفة الاستيفاء في Triangle و Quadrilateral الفصول الدراسية ، نضيف عضو بيانات مرجعية في الفصل Element من النوع InterpolationElement, ، هذا هو:

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

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

ثم نقوم بإنشاء طريقة (كما هو موضح من قبل Scott Meyers ، فعالة 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();
  }
};

هناك مزايا هنا:

  • لم يعد من الضروري تغيير مُنشئ كل من الكائنات المشتقة
  • لم يعد كائن الإستراتيجية 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();
}

ملخص:

  • يمكنك بسهولة تبديل فئة الاستيفاء لكل عنصر إذا لزم الأمر.
  • لا يوجد وصول مزدوج VTA (الأول لحساب Element ثم لطرق InterpolationElement Intepolte) كما في مثال Mattheu. يعرف كل عنصر في وقت الترجمة الذي يستخدمه فئة الاستيفاء.
  • element_impl هو جزء قبيح ، لكنه ينقذنا من Copypasta. يمكنك توسيعه بشكل أكبر من خلال تنفيذ أغلفة طريقة الاستيفاء
  • http://en.wikipedia.org/wiki/curially_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