2013-06-20 2 views
8

Ich schreibe eine Audio-Verarbeitung Standalone-Anwendung. Ich habe ein AudioManager Objekt, das Dinge mit der Engine zu tun hat (wie E/A-Gerätemanagement, Signalverarbeitungsrouting, Laufstatus). Ich schreibe eine GUI, um die AudioManager zu steuern, die im Hintergrund läuft. Derzeit benötigt jede Komponente, die die Nachricht senden muss, einen Zeiger darauf.Wie vermeide ich die Verwendung eines globalen für die Daten hinter einer GUI?

Das beginnt verrückt zu erhalten, wenn ein tief verschachteltes Objekt einen Zeiger auf die AudioManager braucht, da es bedeutet, dass ich den Zeiger durch die Konstrukteure von GUI-Objekte übergeben müssen, die über AudioManager (nur einige Subkomponenten müssen nicht direkt kümmern wissen).

Ich könnte einfach AudioManager ein Singleton machen, um die Boilerplate zu vermeiden, aber der Informationsfluss von der Klasse ist bidirektional, so ist dies wahrscheinlich eine schlechte Idee. Ich fühle mich auch ein wenig fischig, alles in eine große Klasse einzuwickeln, aber es macht es ein bisschen leichter, herumzugehen. Gibt es ein allgemeines Muster, um zu vermeiden, dass der hirnlose Zeiger passiert?

Unten ist ein bisschen Pseudocode, der einige Konstruktoren zeigt, die den grundlegenden Typ des Problems hervorheben. Ich habe dieses C++ 11 markiert, um zu sehen, ob das irgendwelche einzigartigen Lösungen gibt.

+5

Also Audio-Manager ist, was Kinder einen Audio-Player in diesen Tagen nennen. Interessant. –

+1

So Trolling ist was Kinder Mach es SO in diesen Tagen. Interessant. – learnvst

+0

Roboter ist nicht "Trolling", er verlockt zur Lächerlichkeit, Dinge "Manager" zu nennen. Das ist fast immer ein Zeichen für schreckliches Design. Sie haben sogar eine Antwort, die das ausdrücklich sagt. –

Antwort

8

In Ihrem Beispiel sollte "SomeWidget" seine tatsächliche Abhängigkeit nehmen, "SomeThingy", kein AudioManager.

Normalerweise, wenn Sie die ganze Welt sehen, die eine Klasse referenziert, bedeutet es, dass die Klasse zu viel tut. Der Name "XyzManager" weist normalerweise auf ein Problem aus demselben Grund hin. (Klassen sollten nach benannt werden, was sie tun, und wenn die meisten bestimmten Namen zur Verfügung, die beschreibt, was es tut, ist „Manage“, dann sollte es getrennte Klassen)

2

Dependecy injection helfen könnte. Es hilft auch bei der Klärung von Eigentumsfragen und Sie erhalten eine bessere Testbarkeit kostenlos, da es einfach ist, Klassen überzuspionieren.

Die Idee ist, alle Ihre Ressourcenzuweisungen in die Fabrik zu verschieben; Ihre Klassen-Ctors nehmen nur (intelligente) Zeiger auf ihre unmittelbaren Abhängigkeiten.

Etwas in diese Richtung:

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

Nun wird die Komplexität in Ihrer Fabrik Klasse zeigen, aber zumindest das Chaos an einen Ort beschränkt werden.

Wenn die Fabrik zu groß wird, können Sie sie in separate Klassen aufteilen; oder noch besser, haben unterschiedliche Fabriken für unabhängige Dinge: eine Fabrik zur Herstellung von thingies, einem anderen Fabrik für Widgets etc.

ich mit Billy einverstanden ist, einen Manager mit der Umgebung ist das Zeichen einer Klasse versuchen, zu viel zu tun und Das Design sollte überarbeitet werden. Leider, wenn das God Object in einer Third-Party-Bibliothek lebt und Sie keine Kontrolle darüber haben ... :(

+0

Dies ist immer noch eine globale obwohl.Ihr Global wird einfach "Factory" und nicht die AudioManager-Instanz. –

+0

Danke für die interessante Perspektive +1 – learnvst

+0

@BillyONeal Ja, ich stimme zu, ein Manager in der Nähe ist das Zeichen für eine Klasse, die versucht, zu viel zu tun, und das Design sollte überarbeitet werden. Was die Fabrik betrifft, können Sie eine große, riesige Fabrik in separate Klassen aufteilen; oder noch besser, haben verschiedene Fabriken für nicht verwandte Dinge, eine Fabrik, Dinger, eine andere für Widgets usw. – Ali

Verwandte Themen