2010-07-22 21 views
16

Ho una classe base astrattametodi astratti statici in C++

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

voglio dire - "tutte le classi che forniscono un'esemplificazione concreta devono fornire questi metodi statici troppo"

Sono tentato di fare

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

So che non viene compilato e comunque non è chiaro come utilizzarlo anche se è stato compilato. E comunque posso solo fare

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

senza menzionare Fabbrica nella classe base affatto.

Ma credo che ci dovrebbe essere un modo di esprimere il contratto a cui voglio che le implementazioni si iscrivano.

Esiste un linguaggio ben noto per questo? O lo metto nei commenti? Forse non dovrei provare a forzare questo comunque

Modifica: ho potuto sentire me stesso essere vago come ho digitato la domanda. Ho appena sentito che dovrebbe esserci un modo per esprimerlo. Igor dà una risposta elegante ma in realtà dimostra che in realtà non aiuta. Ho ancora finire dover fare

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

Credo lingue riflettenti come C#, Python o Java potrebbe offrire una soluzione migliore

+3

Non sono abbastanza sicuro di quello che vuoi. Stai cercando un metodo 'clone'? – GManNickG

+2

Immagino di non vedere davvero una situazione del mondo reale in cui avresti bisogno di qualcosa del genere? Il polimorfismo ha davvero senso solo con istanze reali. I metodi statici sono solo funzioni regolari che usano il nome della classe per MRO. Non ha davvero senso, a un certo punto devi conoscere il nome del metodo class ::, perché C++ è collegato staticamente. – rossipedia

+1

Sembra che voglia specificare che le sottoclassi hanno un metodo Factory statico. E Bryan, dovresti spostarlo nello spazio di risposta. – jdmichal

risposta

27

Il problema che si sta verificando è in parte dovuto a una lieve violazione un unico principio di responsabilità. Stavi cercando di far rispettare la creazione dell'oggetto tramite l'interfaccia. L'interfaccia dovrebbe invece essere più pura e contenere solo metodi che sono parte integrante di ciò che l'interfaccia dovrebbe fare.

Invece, è possibile rimuovere la creazione dall'interfaccia (il metodo virtual static desiderato) e inserirla in una classe di fabbrica.

Ecco una semplice implementazione di fabbrica che forza un metodo factory su una classe derivata.

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

utilizzati:

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

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

In derinving dal Factory<TClass, TInterface>, la classe derivata è costretto ad avere un metodo CreateInternal dal compilatore. Non deifining che si tradurrà in un errore come questo:

errore C2039: 'CreateInternal': non è un membro della

0

Non esiste un modo sicuro per prescrivere un tale contratto in C++, in quanto v'è anche non c'è modo di utilizzare questo tipo di polimorfismo, poiché la linea

Concrete::Factory() 

è sempre una cosa statica in fase di compilazione, cioè non è possibile scrivere questa linea dove Concrete sarebbe una classe client fornito ancora sconosciuti.

È possibile rendere i client implementare questo tipo di "contratto" rendendolo più conveniente rispetto a non fornirlo. Ad esempio, è possibile utilizzare CRTP:

class IThingy {...}; 

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

e dire ai clienti di derivati ​​da AThingy<their_class_name> (si potrebbe applicare questo con il costruttore visibilità tweaking, ma non è possibile garantire che i clienti non mentono su their_class_name).

Oppure è possibile utilizzare la soluzione classica, creare una gerarchia separata di classi factory e chiedere ai client di fornire il proprio oggetto ConcreteFactory all'API.

+0

"non è possibile scrivere questa riga in cui Concrete sarebbe una classe fornita dal client ancora sconosciuta." È possibile, se si dispone di un mezzo per registrare i costruttori di tipi con la fabbrica. Lo faccio sempre quando nel mio livello di messaggistica conosco solo i miei tipi astratti, ma l'utilizzo di una factory può costruire e restituire istanze di tipi definiti in un'altra libreria. –

+0

Scusate, avrei dovuto dire che non potete farlo direttamente da 'IThingy' se' Concrete' non è stato definito, come avete menzionato. Intendevo dire che è necessario impartire un mezzo per "IThingy" per istanziare indirettamente 'Concrete'. –

0

metodi statici 'Thingy' non può essere fatta virtuale (o astratta, per importa) in C++.

Per fare ciò che si intende, è possibile avere un metodo IThingy::factory che restituisce un'istanza concreta, ma è necessario in qualche modo fornire un mezzo per la factory per creare l'istanza. Ad esempio, definire una firma del metodo come IThing* (thingy_constructor*)() e disporre di una chiamata statica in IThingy per cui è possibile passare una funzione di questo tipo a quella che definisce come IThingy costruirà l'istanza di fabbrica. Quindi, in una libreria o classe dipendente, puoi chiamare questo metodo con una funzione appropriata che, a sua volta, nows come costruire correttamente un oggetto che implementa la tua interfaccia.

Supponendo che non sia stato chiamato l'inizializzatore di fabbrica, è necessario prendere le misure appropriate, ad esempio l'eccezione.