2013-06-20 11 views
8

Piszę samodzielną aplikację do przetwarzania dźwięku. Mam obiekt AudioManager, który owija rzeczy związane z silnikiem (takie jak zarządzanie urządzeniami I/O, routing przetwarzania sygnałów, stan pracy). Piszę GUI do kontroli AudioManager działa w tle. Obecnie każdy komponent, który musi wysłać wiadomość AudioManager, potrzebuje do tego wskaźnika.Jak uniknąć używania danych globalnych dla danych za GUI?

ta zaczyna się szalony, gdy głęboko zagnieżdżony obiekt potrzebuje wskaźnik do AudioManager, a to oznacza, że ​​muszę przekazać wskaźnik przez konstruktorów obiektów graficznych, które bezpośrednio nie dbają o AudioManager (tylko niektóre składowe muszą wiedzieć).

Mogę po prostu zrobić AudioManager singleton, aby uniknąć tablicy, ale przepływ informacji z klasy jest dwukierunkowy, więc jest to prawdopodobnie zły pomysł. Czuję się też trochę podejrzanie owijając wszystko w wielką klasę, ale sprawia, że ​​łatwiej jest o tym mówić. Czy istnieje wspólny wzorzec, aby uniknąć przechodzenia bezmyślnego wskaźnika?

Poniżej znajduje się fragment pseudokodu pokazujący niektóre konstruktory podkreślające podstawowy typ problemu. Tagowałem to C++ 11, aby zobaczyć, czy to daje jakieś unikalne rozwiązania.

MainWindow() 
{ 
    am = new AudioManager(); 
    someWidget = new SomeWidget(am); 
} 

SomeWidget(AudioManager* am_) //SomeWidget does not really care about am 
{ 
    someSubComponent = new SubThingy(am_); 
} 

SubThingy(AudioManager* am_) : subThingyLocalAudioManagerPtr(am_) 
{ 
    subThingyLocalAudioManagerPtr->registerSomethingOrOther(this); 
} 
+5

W dzisiejszych czasach audio-manager jest tym, co dzieci nazywają odtwarzaczem audio.Ten ciekawy –

+1

Więc trolling to co dzieci Zrobić na SO w tych dniach Interesujące – learnvst

+0

Robot nie jest "trollingu", jest pociągający do śmieszności nazywania rzeczy "menedżerami", co jest prawie zawsze oznaką strasznego projektu.Masz nawet odpowiedź, która wyraźnie mówi, że –

Odpowiedz

8

W twoim przykładzie "SomeWidget" powinien wziąć swoją rzeczywistą zależność, "SomeThingy", a nie AudioManager.

Zwykle, gdy widzisz cały świat odnoszący się do klasy, oznacza to, że klasa robi zbyt wiele. Nazwa "XyzManager" zwykle oznacza problem z tego samego powodu. (Klasy powinny być nazywane po tym, co robią, a jeśli najbardziej specyficzna nazwa dostępne, który opisuje to, co robi jest „Zarządzanie”, to powinno być osobne klasy)

2

Dependecy injection może pomóc. Pomaga także w rozwiązywaniu problemów związanych z własnością i zapewnia lepszą testowalność za darmo, ponieważ ułatwia wyśledzenie zajęć.

Chodzi o to, aby przenieść wszystkie zasoby zasobów do fabryki; Twoi członkowie klasy biorą tylko (inteligentne) wskaźniki do swoich bezpośrednich zależności.

Coś wzdłuż tych linii:

#include <memory> 
using namespace std; 

class SubThingy; 

class AudioManager { 
    public: 
    void registerSomethingOrOther(SubThingy* st) { }; 
}; 

// None of the ctors do resource allocation 
class SubThingy { 
    public: 
    SubThingy(AudioManager* am) : subThingyLocalAudioManagerPtr(am) 
    { 
     subThingyLocalAudioManagerPtr->registerSomethingOrOther(this); 
    }; 
    private: 
    // raw pointer, we don't own it 
    AudioManager* subThingyLocalAudioManagerPtr; 
}; 

class SomeWidget { 
    public: 
    // only takes DIRECT depencies 
    SomeWidget(unique_ptr<SubThingy> st) : someSubComponent(move(st)) { } 
    private: 
    // SomeWidget owns someSubComponent 
    unique_ptr<SubThingy> someSubComponent; 
}; 

class MainWindow { 
    public: 
    // only takes DIRECT depencies 
    MainWindow(unique_ptr<SomeWidget> sw) : someWidget(move(sw)) { } 
    private: 
    // MainWindow owns its widgets 
    unique_ptr<SomeWidget> someWidget; 
}; 

class Factory { // All memory allocations happen in the factory 
    public: 
    static unique_ptr<MainWindow> createMainWindow(AudioManager* am) 
    { 
     unique_ptr<SubThingy> someSubComponent{ new SubThingy(am) }; 

     unique_ptr<SomeWidget> someWidget{ new SomeWidget(move(someSubComponent)) }; 

     return unique_ptr<MainWindow>(new MainWindow(move(someWidget))); 
    } 
}; 

int main() { 
    // not clear from the example who owns/should own the audio manager 
    AudioManager* am = nullptr; 

    auto mainWindow{ Factory::createMainWindow(am) }; 
} 

Teraz złożoność pojawi się w swojej klasie fabrycznych ale przynajmniej bałagan zostanie ograniczony do jednego miejsca.

Jeśli fabryka jest zbyt duża, można ją podzielić na odrębne klasy; lub jeszcze lepiej, mają różne fabryki dla niezwiązanych ze sobą rzeczy.: fabryka produkująca rzeczy, inna fabryka na widżety itp.

Zgadzam się z Billym, posiadanie menedżera jest oznaką klasy, która próbuje zrobić zbyt wiele i projekt powinien zostać zmieniony. Niestety, jeśli obiekt Boga znajduje się w bibliotece osób trzecich, a Ty nie masz nad nim kontroli ... :(

+0

To wciąż jest jednak globalne.Twoje globalne staje się "fabryczne", a nie instancja AudioManagera. –

+0

Dzięki za interesującą perspektywę +1 – learnvst

+0

@BillyONeal Tak, zgadzam się, posiadanie menedżera wokół jest oznaką klasy próbującej zrobić za dużo, a projekt powinien zostać zmieniony. Jeśli chodzi o fabrykę, możesz podzielić dużą, ogromną fabrykę na osobne klasy; a nawet lepiej, mają różne fabryki dla niezwiązanych ze sobą rzeczy, fabryka produkująca rzeczy, inna dla widżetów itp. – Ali

Powiązane problemy