Domanda

Supponiamo di avere una classe astratta Element da quali classi Triangle E Quadrilateral sono derivati ​​da.

Supponiamo ancora che queste classi vengano utilizzate insieme a metodi di interpolazione che dipendono dalla forma dell'elemento.Quindi, fondamentalmente creiamo una classe astratta InterpolationElement da cui deriviamo InterpolationTriangle E InterpolationQuadrilateral.

Quindi, per includere la funzionalità di interpolazione nel file Triangle E Quadrilateral classi, aggiungiamo un membro dati const-reference nella classe Element di tipo InterpolationElement, questo è:

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

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

Creiamo quindi un metodo (come descritto da Scott Meyers, Effective C++) che istanzia un oggetto statico locale di classe InterpolationTriangle COME

const InterpolationTriangle& getInterpolationTriangle()
{
    static InterpolationTriangle interpolationTriangle;

    return interpolationTriangle;
}

Quindi quella classe Triangle può essere costruito come:

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

Ecco la mia domanda:questo approccio è corretto per incorporare metodi di interpolazione nella mia classe Element?Viene utilizzato in scenari professionali?

Potrei implementare direttamente tutti i metodi di interpolazione sulla classe Element (come puro virtuale) e sovrascriverli nelle classi derivate Triangle E Quadrilateral.Tuttavia, questo approccio mi sembra macchinoso, poiché ogni volta che ho bisogno di migliorare o implementare nuove funzionalità di interpolazione dovrei farlo su queste classi.Inoltre, le classi diventano sempre più grandi (molti metodi) utilizzando questo approccio.

Vorrei sentire da voi alcuni suggerimenti e commenti

Grazie in anticipo.


Dettagli aggiuntivi:

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}
}
È stato utile?

Soluzione

Le classi vengono utilizzate insieme all'interpolazione metodi.Perché questi metodi devono trovarsi in un oggetto singleton?Il singleton qui sembra molto problematico.

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

Inoltre, benvenuto su SO!

Altri suggerimenti

Questo ricorda una domanda a cui avevo risposto Qui.La stessa idea riguardo alla separazione dei contenitori di dati e alle strategie.

C'è un piccolo problema con la tua proposta:hai aggiunto un metodo relativo all'interpolazione alla tua classe base e hai cambiato il costruttore...

Quindi prima di tutto, se desideri farlo in questo modo, ecco come dovresti farlo:

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

Ci sono 2 vantaggi qui:

  • Non è più necessario modificare il costruttore di ciascuno degli oggetti derivati
  • L'oggetto della strategia non esiste più const, che gli consente di mantenere lo stato durante il calcolo...come un riferimento all'oggetto corrente da interpolare.

Tuttavia, ciò richiede ancora di modificare il file Element classe e ciascuna delle sue classi derivate.Non ti dà fastidio ;) ?

Bene, è il momento (per una volta) di ricorrere a un Design Pattern: Visitor.

È un po' diverso dall'idea strategica, che fa affidamento sul doppio invio per funzionare correttamente.Tuttavia ti consente di modificare la gerarchia di Elements UNA VOLTA (con un accept metodo) e quindi aggiungere tutte le operazioni che desideri.E questo è fantastico.

Puoi sempre pasticciare un po' con i modelli.Innanzitutto abbiamo una classe superiore.

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

...ma poi abbiamo anche una classe al centro della gerarchia che in realtà è un modello.Il modello non può essere la classe di livello superiore, poiché i modelli con parametri diversi sono classi diverse.L'idea è di fornire una classe di interpolazione come parametro di tipo all'elemento.

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

E classi di interpolazione.Nota: non sono fratelli, perché non ne hanno bisogno.

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

E infine gli elementi reali e la piccola procedura principale.

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

Riepilogo:

  • puoi facilmente cambiare classe di interpolazione per ciascun elemento, se necessario.
  • non ci sono doppi accessi a vtable (prima per il calcolo di Element e poi per i metodi di interpolazione di InterpolationElement) come nell'esempio di Matthieu.Ogni elemento sa in fase di compilazione quale classe di interpolazione sta utilizzando.
  • Element_Impl è un po' brutto, ma ci salva dal copypasta.È possibile espanderlo ulteriormente implementando i wrapper del metodo di interpolazione
  • http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Un modo è utilizzare metodi statici e definire un wrapper in Element_Impl, sempre in un solo posto.

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

La prima cosa che mi viene in mente è il Visitatore del modello di progettazione GoF

Da quello che ho capito del tuo problema, questo modello è concepito per risolvere esattamente questo problema.

Ogni oggetto Visitor definisce una tecnica di interpolazione o un algoritmo da applicare al tuo oggetto.

Pertanto la classe Element non cresce affatto con ogni nuova funzionalità.Una volta installato, il pattern Visitor consente di arricchire le funzionalità senza toccare la definizione della classe Base.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top