2016-01-11 26 views
6

Pracuję z biblioteką zewnętrzną i muszę utworzyć wzorzec obserwatora, w którym obserwatorzy pochodzą z obiektu należącego do biblioteki. Nie chcę zmieniać klasy bazowej z biblioteki, a jednocześnie muszę używać listy referencji/wskaźników do tej niezmiennej klasy bazowej. Ponadto biblioteka tworzy listę obiektów, z których muszę przesiać te, które nadają się do obserwowania.Używanie RTTI z obiektami pochodzącymi z biblioteki zewnętrznej

Kod pisałem to z grubsza odpowiednik tego:

#include <iostream> 
#include <vector> 
#include <memory> 

// This class is from an external library which I don't want to chagne 
class BaseFromLibrary { 
    public: 
    virtual ~BaseFromLibrary() {} 
}; 

class BaseOfObserver { 
    public: 
    void notify() { std::cout << "What-ho!\n"; }; 
}; 

class Observer : public BaseFromLibrary, public BaseOfObserver {}; 

class Subject { 
    public: 
    std::vector<std::shared_ptr<Observer>> observers; 
    void notifyObervers() { 
     for (auto &o : observers) 
      (*o).notify(); 
    } 
}; 

int main() { 
    // This list is constructed by the library and I cannot interfere with that 
    // process 
    std::vector<std::shared_ptr<BaseFromLibrary>> list{ 
     std::make_shared<BaseFromLibrary>(), std::make_shared<Observer>()}; 
    Subject s; 

    for (auto &e : list) 
     if (std::dynamic_pointer_cast<Observer>(e) != nullptr) 
      s.observers.push_back(std::dynamic_pointer_cast<Observer>(e)); 

    s.notifyObervers(); 
} 

Następnie używam BaseOfObserver dodać „osobnik” świadomości do innych moich typów pochodnych. W ten sposób nie muszę powtarzać instrukcji if dla każdego konkretnego obserwatora, który chcę zaimplementować.

Wygląda na to, że działa dobrze, ale czy to błąd w projektowaniu? Czy istnieje lepszy sposób tworzenia listy obserwatorów bez mechanizmu RTTI i bez ingerencji w klasy biblioteki?

Odpowiedz

0

Twój problem sprowadza się do posiadania sekwencji podstawowych wskaźników i chcesz używać tylko takich elementów sekwencji, które są konkretnym typem pochodnym.

dynamic_cast z pewnością działa, ale w tym przypadku nie jest konieczne. Jeśli zachowałeś pożądane wskaźniki w oddzielnym pojemniku, nie musiałbyś szukać ich w pojemniku zawierającym również inne wskaźniki.

std::vector<Observer *> observers {new Observer()}; 

Możesz nadal mieć kontener ze wszystkimi wskaźnikami, kopiując wskaźniki obserwatora.

std::vector<BaseFromLibrary *> all {new BaseFromLibrary()}; 
all.reserve(all.size() + observers.size()); 
all.insert(all.end(), observers.begin(), observers.end()); 

Alternatywnie, można użyć coś takiego boost::join z Boost.Range jeśli chcesz iteracyjne nad wszystkie elementy w osobnych pojemnikach bez kopiowania.

Możesz trywialnie kopiować wskaźniki obserwatora bez funkcji dynamic_cast.

s.observers.insert(observers.begin(), observers.end()); 

Oczywiście, jeśli to wszystko, co zamierzamy zrobić, to mogło być wykorzystywane jako s.observers oryginalnym opakowaniu dla obserwatorów w pierwszej kolejności.

Jako przykładowy przykładowy program wycieka z pamięci. Unikaj ręcznego zarządzania pamięcią, aby zapobiec tym sytuacjom.

+0

Jeśli dobrze Cię rozumiem, sugerujesz podział "listy" z mojego przykładu. Być może zapomniałem podkreślić, że jest to również coś, czego nie mogę zrobić. Biblioteka sama tworzy tę listę i nie chcę ingerować w ten proces. Spróbuję to edytować. Punkt zajęty wyciekiem pamięci. –