2009-06-24 13 views
7

Vorrei creare una classe base (astratta) (chiamiamola type::base) con alcune funzionalità comuni e un'interfaccia fluente, il problema che sto affrontando è il tipo di ritorno di tutti quei metodiInterfacce fluide ed ereditarietà in C++

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

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

Ora ho potuto fare sottotipi, ad esempio:

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

Il problema arriva quando si utilizzano questi sottotipi come questo:

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

Questo non verrà compilato perché stiamo restituendo base anziché my_type.

so che ho potuto solo:

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

    return ret; 
} 

Ma stavo pensando come posso implementarlo, ed io non ho trovato tutte le idee valide, qualche suggerimento?

+0

Ho rimosso lo spazio dei nomi perché non ha nulla (per quanto posso vedere) da fare con la domanda –

risposta

4

Questo problema di "perdere il tipo" può essere risolto con i modelli, ma è piuttosto complicato.

Es.

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

È quindi possibile scrivere

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

Il modello è che invece di

class T : public B 

si scrive

class T : public Fluent<T, B> 

Un altro approccio non è stato possibile usare l'interfaccia fluente su gli oggetti, ma su puntatori invece:

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

Usare in questo modo:

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

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

Potresti fornire un esempio? Potrebbe essere interessante. – liori

+0

cf. la modifica. Potrebbe essere interessante, non so se lo userò personalmente, però ... – jpalecek

+0

Quindi in effetti passi ai puntatori. Quindi puoi semplicemente usare il polimorfismo semplice, senza CRTP e return base *. – liori

-2

Il modo in cui lo farei in C#, e credo che avrebbe funzionato in C++ troppo è di fornire un'implementazione di default per with_foo() e with_bar() ... Perdonate la mia C#, ma:

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

Ma questo non risolve il problema - il problema è una mancata corrispondenza di tipo, che è irrilevante della domanda . – jpalecek

-1

In C++ si dovrebbero essere puntatori o riferimenti di reticolazione piuttosto che valori. Inoltre, potresti voler spiegare cosa intendi per "interfacce fluenti".

+2

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

+1

Mi dispiace, non appena vedo il nome "Martin" il mio rilevatore di bullsh * t si spegne a tutto volume e non posso più leggere. Non sembra importare se si tratta di un nome o di un cognome, sia. –

+0

Cosa c'è che non va in Martin Fowler? – Tobias

0

Una soluzione potrebbe funzionare in questo modo:

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

Utilizzando static_cast fondamentalmente dice al compilatore 'Io so quello che sto facendo qui'.

5

Si dovrebbe essere restituendo riferimenti/puntatori, e non dovrebbe essere necessario per mantenere le informazioni sul 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(); 
} 

Hai già un distruttore virtuale. Presumibilmente hai altre funzioni virtuali.Accedi a tutto tramite il tipo di base e le funzioni virtuali dichiarate lì.

+0

Il problema è che non voglio perdere il tipo. – blaxter