2012-12-13 12 views
11

Mam kompozytowego realizację wzór, używany komponentów GUI:Możliwość mieszać kompozytowego wzór i ciekawie cykliczne szablon wzór

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

Niestety liczba dziedziczonych klas wzrasta gwałtownie i kod duplikat (w podanym przykładzie tylko metoda detach()) sprawia mi ból głowy.

Czy istnieje sposób czyszczenia implementacji metod detach(), zachowując typ zwrotu taki sam jak obiekt, na który jest wywoływany?

Myślałam o CRTP, ale nie mogę myśleć o sposób, aby utrzymać polimorfizm dynamiczny wraz z kompilacji polimorfizmu:

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. 

Odpowiedz

6

Możesz dodać jeden poziom abstrakcji:

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

W języku naturalnym: interfejs dla wszystkich obiektów (CObjectBase) ma częściową implementację dla potomków (CObject<Child>), które muszą po prostu odziedziczyć tę częściową implementację, zmniejszając ilość replikowanego kodu.

1

Myślałam o CRTP, ale nie mogę myśleć o sposób, aby utrzymać polimorfizm dynamiczny wraz z polimorfizmem czasu kompilacji

Można mieszać je poprzez dostarczanie domyślne wirtualnych implementacje dla niektórych interfejsów wykorzystujących CRTP style klas bazowych.

Dzięki temu istnieje możliwość agregowania implementacji podstawowych CRTP (może być skonfigurowany z dodatkowymi parametrami "policy"-template) i wciąż jest w stanie zastąpić określone zachowanie w odziedziczonych klasach.

Microsoft's ATL library używa tego dużo. Korzystam również z tej techniki w moim STTCL state machine library.

1

Z samego fragmentu kodu nie jest jasne, dlaczego potrzebujesz detach(), aby zwrócić wskaźnik do dostarczonego typu.

Aby skorzystać z usługi zwracania dostarczonego typu, należy ją wywołać przy użyciu odwołania do dostarczonego typu. Tak:

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

ale to może być zastąpiony odpowiednik, który działa nawet, jeśli odłączy jest nieważna:

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

Jeśli masz odwołanie do typu podstawowego, nie można skorzystać z detach() typ zwrotu:

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

Z tego powodu nie jest jasne, jakie są zalety podejścia, które próbujesz wdrożyć.

Strona nie jest taka, że ​​metoda duplicate() jest śmierdząca. Przerywa, gdy dostarczona klasa nie zastępuje go, ale używa domyślnej implementacji z klasy nadrzędnej. Może to być znak, że coś jest nie tak z projektem wysokiego poziomu.

+0

'odłączy()' metoda jest stosowana w brzydki sposób: 'CObject * tree_of_stuff;' - Pełne drzewo obiektów 'CSpecificObject * specific_object = tree_of_stuff-> Dziecko ("stringID") -> detach(); ' W tym przypadku metoda' Child <>() 'przeszukuje drzewo i rzuca obiekt na określony parametr szablonu. Ta składnia jest niedostępna, jeśli 'detach()' zwraca 'void' lub' CObject * '. –

+1

'duplicate()' metoda jest pewnym źródłem błędów, to jest jeden z powodów, dla których przedłużam bieżący wzór za pomocą CRTP. Moim zdaniem poleganie na konstruktorze kopii jest bezpieczniejsze, jeśli obiecuję, że wszyscy wdrożą metodę 'duplicate()'. –