2012-12-13 11 views
11

ho un'implementazione composite, usato per componenti GUI:Possibilità di mescolare composite e mascherina modello curiosamente ricorrenti

class CObject { 
private: 

    CObject * m_pParent; 
    CObjectContainer * m_pChildren; 

    void private_foo() { 
    this->foo(); 
    //Calls private_foo for each child in container. 
    m_pChildren->foo(); 
    } 

public: 
    virtual void foo() { 
    //empty for base class 
    } 

    virtual CObject * duplicate() { 
    //Do duplication code 
    return new CObject(*this); 
    } 

    virtual CObject * detach() { 
    //Remove this object (along with it's children) 
    //from current tree. 
    m_pParent->RemoveChild(this); 
    m_pParent = nullptr; 
    return this; 
    } 
} 

class CSpecificObject : public CObject { 
public: 
    virtual void foo() { 
    //Specific code for this class 
    } 

    virtual CSpecificObject * duplicate() { 
    //Overload, but the code only calls diferent constructor 
    return new CSpecificObject(*this); 
    } 

    virtual CSpecificObject * detach() { 
    //Note the code is identical. 
    m_pParent->RemoveChild(this); 
    m_pParent = nullptr; 
    return this; 
    } 
} 

Purtroppo il numero di classi ereditate aumenta rapidamente e il codice duplicato (in data esempio, solo il il metodo detach()) mi sta facendo venire il mal di testa.

C'è un modo per implementare in modo pulito i metodi detach(), mantenendo il tipo restituito uguale all'oggetto, sul quale viene chiamato?

Stavo pensando di CRTP, ma non riesco a pensare a un modo per mantenere il polimorfismo dinamico con compilazione tempo polimorfismo:

template <Child> 
class CObject { 
private: 
    ... 
    Child * detach() { 
    m_pParent->RemoveChild(this); 
    m_pParent = nullptr; 
    return static_cast<Child*>(this); 
    } 
    ... 
} 

//Array of CObject* pointers is no longer possible. 

risposta

6

È possibile aggiungere un livello di astrazione:

class CObjectBase 
{ 
    public: 
     // Other methods... 
     virtual CObjectBase* detach() = 0; 
     virtual CObjectBase* duplicate() const = 0; 
}; 

template <typename Child> 
class CObject : public CObjectBase 
{ 
    public: 
     // ... 
     Child* duplicate() const 
     { 
      return new Child(*static_cast<Child*>(this)); 
     } 

     Child* detach() 
     { 
      m_pParent->RemoveChild(this); 
      m_pParent = nullptr; 
      return static_cast<Child*>(this); // Cast needed here (inherent to CRTP) 
     } 
     std::vector<CObjectBase*> children; // Array possible now 
     // ... 
}; 

class MyObject : public CObject<MyObject> 
{ 
    // ... 
}; 

In linguaggio naturale: un'interfaccia per tutti gli oggetti (CObjectBase) ha un'implementazione parziale per i suoi discendenti (CObject<Child>), che devono solo ereditare questa implementazione parziale, diminuendo la quantità di codice replicato.

1

Stavo pensando di CRTP, ma non riesco a pensare a un modo per mantenere il polimorfismo dinamico insieme con il tempo il polimorfismo di compilazione

Si possono mescolare fornendo predefinite implementazioni virtuali per alcune interfacce che utilizzano CRTP classi base di stile.

Così si ha la possibilità di aggregare le implementazioni di base CRTP (magari configurate con parametri di modello 'policy' aggiuntivi) ed essere ancora in grado di sovrascrivere un comportamento particolare nelle classi ereditate.

Microsoft's ATL library utilizza questo molto. Mi avvalgo anche di questa tecnica nel mio STTCL state machine library.

1

Dal snippet da solo non è chiaro il motivo per cui è necessario detach() per restituire un puntatore a un tipo consegnato.

Per usufruire di detach() restituendo un tipo consegnato, è necessario chiamarlo utilizzando comunque un riferimento al tipo consegnato. Come questo:

CSpecificObject* specific_object = new SpecificObject(); 
// ... 
specific_object->detach()->method_declared_in_specific_object(); 

Ma questo può essere sostituita con equivalente che funziona anche se detach è nullo:

specific_object->detach(); 
specific_object->method_declared_in_specific_object(); 

Se si dispone di un riferimento al tipo di base, non è possibile usufruire di detach() tipo di ritorno:

CObject* specific_object = new SpecificObject(); 
//... 
// !!! Won't compile: 
specific_object->detach()->method_declared_in_specific_object(); 

Per questo motivo non è chiaro quali sono i vantaggi dell'approccio che si sta tentando di implementare.

Un lato non è che il metodo duplicate() è maleodorante. Si interrompe quando la classe consegnata non la sovrascrive, ma utilizza l'implementazione predefinita dalla classe genitore. Può essere un segno che qualcosa non va nel design di alto livello.

+0

'detach()' metodo viene utilizzato in maniera brutta: 'CObject * tree_of_stuff;' - Un albero pieno di oggetti 'CSpecificObject * specific_object = tree_of_stuff-> Bambino ("stringID") -> detach(); ' Qui il metodo' Child <>() 'esegue una ricerca lungo l'albero e lo proietta sul parametro template specificato. Questa sintassi non è disponibile se 'detach()' restituisce 'void' o' CObject * '. –

+1

Il metodo 'duplicate()' è una certa fonte di errori, questo è uno dei motivi per cui sto estendendo il modello corrente con CRTP. A mio avviso, affidarsi a copy constructor è più sicuro che, con la promessa che tutti implementeranno il metodo 'duplicate()'. –

Problemi correlati