2010-07-22 19 views
16

que tienen una clase base abstractamétodos abstractos estáticos en C++

class IThingy 
{ 
    virtual void method1() = 0; 
    virtual void method2() = 0; 
}; 

quiero decir - "todas las clases que proporcionan una ejemplificación concreta deben proporcionar estos métodos estáticos también"

me siento tentado a hacer

class IThingy 
{ 
    virtual void method1() = 0; 
    virtual void method2() = 0; 
    static virtual IThingy Factory() = 0; 
}; 

Sé que no compila, y de todos modos no está claro cómo usarlo, incluso si compiló. Y de todos modos puedo simplemente hacer

Concrete::Factory(); // concrete is implementation of ITHingy 

sin mencionar fábrica en la clase base en absoluto.

Pero creo que debería haber alguna manera de expresar el contrato al que quiero que firmen las implementaciones.

¿Hay un idioma bien conocido para esto? ¿O simplemente lo pongo en comentarios? Tal vez no debería intentar forzar esto de todos modos

Editar: Pude sentirme vago mientras escribía la pregunta. Siento que debería haber alguna forma de expresarlo. Igor da una respuesta elegante, pero de hecho muestra que realmente no ayuda. Todavía acaban de tener que hacer

IThingy *p; 
    if(..) 
     p = new Cl1(); 
    else if(..) 
     p = new Cl2(); 
    else if(..) 
     p = new Cl3(); 
    etc. 

supongo idiomas reflectantes, como C#, Python o Java podría ofrecer una mejor solución

+3

No estoy muy seguro de lo que quiere. ¿Estás buscando un método 'clon'? – GManNickG

+2

Supongo que realmente no veo una situación en el mundo real en la que necesites algo así? El polimorfismo solo tiene sentido en instancias reales. Los métodos estáticos son solo funciones regulares que usan el nombre de clase para MRO. Realmente no tiene sentido, en algún momento debe conocer el nombre del método class :: porque C++ está vinculado estáticamente. – rossipedia

+1

Parece que quiere especificar que las subclases tengan un método Factory estático. Y Bryan, deberías mover eso al espacio de respuestas. – jdmichal

Respuesta

27

El problema que está teniendo tiene que ver en parte con una leve violación de un solo principio de responsabilidad. Intentaba imponer la creación del objeto a través de la interfaz. La interfaz debería ser más pura y solo contener métodos que sean integrales a lo que se supone que debe hacer la interfaz.

En su lugar, puede sacar la creación de la interfaz (el método virtual static deseado) y ponerla en una clase de fábrica.

Aquí hay una implementación simple de fábrica que obliga a un método de fábrica en una clase derivada.

template <class TClass, class TInterface> 
class Factory { 
public: 
    static TInterface* Create(){return TClass::CreateInternal();} 
}; 

struct IThingy { 
    virtual void Method1() = 0; 
}; 

class Thingy : 
    public Factory<Thingy, IThingy>, 
    public IThingy { 
     //Note the private constructor, forces creation through a factory method 
     Thingy(){} 
public: 
     virtual void Method1(){} 
     //Actual factory method that performs work. 
     static Thingy* CreateInternal() {return new Thingy();} 
}; 

Uso:

//Thingy thingy; //error C2248: 'Thingy::Thingy' : cannot access private member declared in class 'Thingy' 

IThingy* ithingy = Thingy::Create(); //OK 

Por derinving de Factory<TClass, TInterface>, la clase derivada es forzada a tener un método CreateInternal por el compilador. No deifining que dará lugar a un error como este:

C2039: 'CreateInternal': no ​​es un miembro de

0

No hay manera segura de prescribir un contrato de este tipo en C++, ya que también existe no hay manera de utilizar este tipo de polimorfismo, ya que la línea

Concrete::Factory() 

es siempre una cosa en tiempo de compilación estática, es decir, no se puede escribir esta línea en la que Concrete habría una clase de cliente proporcionado por el aún desconocido.

Puede hacer que los clientes implementen este tipo de "contrato" al hacerlo más conveniente que no proporcionarlo. Por ejemplo, se puede usar CRTP:

class IThingy {...}; 

template <class Derived> 
class AThingy : public IThingy 
{ 
public: 
    AThingy() { &Derived::Factory; } // this will fail if there is no Derived::Factory 
}; 

y decirle a los clientes a derivados de AThingy<their_class_name> (se podía hacer cumplir este con pellizcar la visibilidad del constructor, pero no se puede asegurar que los clientes no se encuentran sobre their_class_name).

O podría utilizar la solución clásica, crear una jerarquía separada de clases de fábrica y solicitar a los clientes que proporcionen su objeto ConcreteFactory a su API.

+0

"no se puede escribir esta línea donde Concrete sería una clase aún desconocida proporcionada por el cliente". Puede, si tiene un medio para registrar constructores de tipo con la fábrica. Hago esto todo el tiempo, donde en mi capa de mensajería solo conozco mis tipos abstractos, pero al utilizar una fábrica puedo construir y devolver instancias de tipos definidos en otra biblioteca. –

+0

Disculpe, debería haber dicho que no puede hacer esto directamente desde 'IThingy' si' Concrete' no se ha definido, como ha mencionado. Quise decir que necesitas impartir un medio para 'IThingy' para crear una instancia indirecta de' Concreto '. –

0

Los métodos estáticos 'cosita' no se puede hacer virtual (o abstracta, por eso importa) en C++.

Para hacer lo que pretende, puede tener un método IThingy::factory que devuelva una instancia concreta, pero debe proporcionar de algún modo un medio para que la fábrica cree la instancia. Por ejemplo, defina una firma de método como IThing* (thingy_constructor*)() y tenga una llamada estática en IThingy que le permita pasar esa función a la que define cómo IThingy construirá la instancia de fábrica. Luego, en una biblioteca o clase dependiente, puede llamar a este método con una función apropiada que, a su vez, sepa cómo construir correctamente un objeto que implemente su interfaz.

Supongamos que no se ha llamado al inicializador de su fábrica, querría tomar las medidas adecuadas, como lanzar una excepción.

Cuestiones relacionadas