2009-06-24 8 views
7

Me gustaría construir una clase base (abstracta) (llamémosla type::base) con alguna funcionalidad común y una interfaz fluida, el problema al que me enfrento es el tipo de devolución de todos esos métodosInterfaces fluidas y herencia en C++

class base { 
    public: 
     base(); 
     virtual ~base(); 

     base& with_foo(); 
     base& with_bar(); 
    protected: 
     // whatever... 
    }; 

Ahora podría hacer subtipos, por ejemplo:

class my_type : public base { 
    public: 
     myType();   
     // more methods... 
    }; 

El problema viene cuando se utilizan esos subtipos como este:

my_type build_my_type() 
{ 
    return my_type().with_foo().with_bar(); 
} 

Esto no se compilará porque estamos devolviendo base en lugar de my_type.

sé que acabo de poder:

my_type build_my_type() 
{ 
    my_type ret; 
    ret.with_foo().with_bar(); 

    return ret; 
} 

Pero estaba pensando cómo puedo ponerlo en práctica, y no he encontrado ninguna ideas válidas, alguna sugerencia?

+0

He eliminado las cosas del espacio de nombres porque no tiene nada (por lo que puedo ver) que ver con la pregunta –

Respuesta

4

Este problema de "perder el tipo" se puede resolver con plantillas, pero es bastante complicado.

Por ejemplo.

class Pizza 
{ 
    string topping; 
public: 
    virtual double price() const; 
}; 

template <class T, class Base> 
class FluentPizza : public Base 
{ 
    T* withAnchovies() { ... some implementation ... }; 
}; 

class RectPizza : public FluentPizza<RectPizza, Pizza> 
{ 
    double price() const { return length*width; :) } 
}; 

class SquarePizza : public FluentPizza<SquarePizza, RectPizza> 
{ 
    ... something else ... 
}; 

continuación, puede escribir

SquarePizza* p=(new SquarePizza)->withAnchovies(); 

El patrón es que en lugar de

class T : public B 

se escribe

class T : public Fluent<T, B> 

Otro enfoque podría no ser el uso de interfaz fluida en los objetos, pero en lugar de punteros:

class Pizza { ... }; 
class RectPizza { ... }; 
class SquarePizza { ... whatever you might imagine ... }; 

template <class T> 
class FluentPizzaPtr 
{ 
    T* pizza; 
public: 
    FluentPizzaPtr withAnchovies() { 
    pizza->addAnchovies(); // a nonfluent method 
    return *this; 
    } 
}; 

Use la siguiente manera:

FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... } 

FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies(); 
+0

¿Podría darnos un ejemplo? Podría ser Interesante. – liori

+0

cf. la edición. Puede ser interesante, no sé si lo usaría personalmente, aunque ... – jpalecek

+0

Así que realmente cambias a punteros. Entonces puedes usar simplemente polimorfismo simple, sin CRTP, y devolver base *. – liori

-2

La forma en que lo haría en C#, y yo creo que funcionaría en C++ también es proporcionar una implementación por defecto para with_foo() y with_bar() ... Perdona mi C#, pero:

class base { 
    virtual base with_foo() 
    { throw new NotImplementedException(); } 
    virtual base with_bar(); 
    { throw new NotImplementedException(); } 
} 
+0

Pero esto no soluciona el problema - el problema es un desajuste de tipo, que es irrelevante para la pregunta . – jpalecek

-1

En C++ debería estar redefiniendo punteros o referencias en lugar de valores. Además, es posible que desee explicar lo que quiere decir con "interfaces fluidas".

+2

http://en.wikipedia.org/wiki/Fluent_interface – liori

+1

Lo siento, tan pronto como veo el nombre "Martin" mi detector bullsh * t se apaga a todo volumen y no puedo leer más. No parece importar si es un nombre de pila o un apellido, tampoco. –

+0

¿Qué pasa con Martin Fowler? – Tobias

0

Una solución sería el siguiente:

return *static_cast<my_type*>(&my_type().with_foo().with_bar()); 

Usando static_cast dice básicamente el compilador 'Sé lo que estoy haciendo aquí'.

5

Usted debe devolver referencias/punteros, y que no es necesario para mantener la información de tipo.

class base { 
    public: 
    base(); 
    virtual ~base(); 

    base &with_foo(); 
    base &with_bar(); 
    protected: 
    // whatever... 
}; 

class my_type : public base { 
    public: 
    my_type();   
    // more methods... 
}; 

base *build_my_type() 
{ 
    return &new my_type()->with_foo().with_bar(); 
} 

Ya tiene un destructor virtual. Es de suponer que tienes otras funciones virtuales.Acceda a todo a través del tipo base y las funciones virtuales declaradas allí.

+0

El problema es que no quiero perder el tipo. – blaxter

Cuestiones relacionadas