Pergunta

Suponha que tenhamos uma classe abstrata Element de quais classes Triangle e Quadrilateral são derivados de.

Suponha que essas classes sejam usadas em conjunto com os métodos de interpolação que dependem da forma do elemento. Então, basicamente, criamos uma classe abstrata InterpolationElement do qual derivamos InterpolationTriangle e InterpolationQuadrilateral.

Então, para incluir a funcionalidade de interpolação no Triangle e Quadrilateral Aulas, adicionamos um membro de dados de const-referência na aula Element do tipo InterpolationElement, isso é:

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

    const InterpolationElement& getInterpolation() const;

private:
    const InterpolationElement& interpolation;
};

Em seguida, criamos um método (conforme descrito por Scott Meyers, C ++ eficaz) que instanciam um objeto estático local de classe InterpolationTriangle Como

const InterpolationTriangle& getInterpolationTriangle()
{
    static InterpolationTriangle interpolationTriangle;

    return interpolationTriangle;
}

Então essa aula Triangle pode ser construído como:

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

Aqui está minha pergunta: essa abordagem está correta para incorporar métodos de interpolação na minha aula Element? Isso é usado em cenários profissionais?

Eu poderia implementar diretamente todos os métodos de interpolação na aula Element (como puro virtual) e os substituí -los nas classes derivadas Triangle e Quadrilateral. No entanto, essa abordagem me parece ser pesada, pois toda vez que preciso melhorar ou implementar novas funcionalidades de interpolação, teria que fazer isso nessas classes. Além disso, as classes ficam cada vez maiores (muitos métodos) usando essa abordagem.

Eu gostaria de ouvir de você algumas dicas e comentários

Desde já, obrigado.


Detalhes adicionais:

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}
}
Foi útil?

Solução

As classes são usadas em conjunto com a interpolação métodos. Por que esses métodos precisam estar em um objeto Singleton? O Singleton aqui parece muito problemático.

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

Além disso, bem -vindo a So!

Outras dicas

Isso lembra uma pergunta que eu havia respondido aqui. A mesma idéia sobre a separação de contêineres de dados e as estratégias.

Há um pequeno problema com sua proposta: você adicionou um método relacionado à interpolação à sua classe base e mudou o construtor ...

Então, antes de tudo, se você deseja fazer dessa maneira, eis como você deve fazer:

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

Existem 2 vantagens aqui:

  • Não é mais necessário mudar o construtor de cada um dos objetos derivados
  • O objeto de estratégia não é mais const, que permite manter o estado durante o cálculo ... como uma referência ao objeto atual que está sendo interpolado.

No entanto, isso ainda é necessário para mudar o Element classe e cada uma de suas classes derivadas. Não te incomoda;)?

Bem, é hora (pela primeira vez) para chamar um padrão de design: Visitor.

É um pouco diferente da idéia da estratégia, contando com o Double Dispatch para funcionar corretamente. No entanto, permite que você ajuste a hierarquia de Elements uma vez (com um accept método) e depois adicionar quantas operações desejar. E isso é ótimo.

Você sempre pode mexer um pouco com modelos. Primeiro, temos uma classe de primeira.

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

... Mas então também temos uma aula no meio da hierarquia, que é na verdade um modelo. O modelo não pode ser a classe de nível superior, pois os modelos com parâmetros diferentes são classes diferentes. A idéia é que damos uma classe de interpolação como um parâmetro de tipo para o elemento.

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

E classes de interpolação. Observe que eles não são irmãos, porque não precisam.

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, finalmente, os elementos reais e o pequeno procedimento principal.

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

Resumo:

  • Você pode alternar facilmente a classe de interpolação para cada elemento, se necessário.
  • Não há acesso duplo vtable (primeiro para o elemento calcular e depois para os métodos de interepolamento do interpolationElement) como no exemplo do Matthieu. Cada elemento sabe no momento da compilação qual classe de interpolação está usando.
  • Element_Impl é uma parte feia, mas nos salva da Copypasta. Você pode expandi -lo ainda mais implementando invólucros de método de interpolação
  • http://en.wikipedia.org/wiki/curiousous_recurring_template_pattern

Uma maneira é usar métodos estáticos e definir um invólucro em Element_IMPL - ainda apenas em um só lugar.

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

O que primeiro vem à minha mente é o Visitante de padrão de design do GOF

Pelo que entendi do seu problema, esse padrão é concebido para resolver exatamente esse problema.

Cada objeto visitante define uma técnica de interpolação ou um algoritmo para aplicar ao seu objeto.

Assim, a classe de elementos não cresce a cada nova função. Uma vez no lugar, o padrão do visitante permite enriquecer a função sem tocar na definição da classe base.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top