2

Ich habe eine Weile im Web gelesen und gesucht, aber keine gute Lösung gefunden. Hier ist, was ich tun möchte:Singletons und Fabriken mit mehreren Bibliotheken in C++

Ich schreibe eine Bibliothek, die eine abstrakte Basisklasse definiert - rufen Sie es IFoo.

class IFoo { 
    public: 
     virtual void doSomething() = 0; 
     virtual ~IFoo() {} 
}; 

Die Bibliothek definiert auch ein paar Implementierungen für das - nennen wir sie FooLibOne und FooLibTwo.

Um den Erstellungsprozess zu kapseln und entscheiden, welche konkrete Umsetzung abhängig von gewisser Laufzeit Paramter verwendet wird, verwende ich eine Fabrik FooFactory die std :: string-Pläne zu Factory-Methoden (in meinem Fall boost :: Funktion, aber das sollte nicht der Punkt hier sein). Außerdem können neue Fabrikmethoden registriert werden. Es sieht etwa so aus:

class FooFactory { 
    public: 
     typedef boost::function<IFoo*()> CreatorFunction; 

     IFoo* create(std::string name); 
     void registerCreator(std::string name, CreatorFunction f); 

    private: 
     std::map<std::string, CreatorFunction> mapping_; 
}; 

Vorerst ich die Implementierungen zur Verfügung gestellt (FooLibOne, FooLibTwo) von der Bibliothek direkt im Konstruktor von FooFactory hinzugefügt - so sind sie immer zur Verfügung. Ein Teil des Bibliothekscodes verwendet die FooFactory, um bestimmte Objekte zu initialisieren usw. Ich habe bisher von der Verwendung des Singleton-Musters für die Fabriken abgesehen, da das T-Muster oft genug diskutiert wird und ich nicht sicher war, wie verschiedene Implementierungen des Singleton-Musters funktionieren würden in Kombination mit möglicherweise mehreren gemeinsam genutzten Bibliotheken usw.

Allerdings kann die Weitergabe der Fabriken ein wenig umständlich sein und ich denke immer noch, dies ist eine der Gelegenheiten, die das Singleton-Muster von Nutzen sein könnte. Vor allem wenn ich bedenke, dass die Benutzer der Bibliothek in der Lage sein sollten, weitere Implementierungen von IFoo hinzuzufügen, die auch für den (bereits vorhandenen) Bibliothekscode zugänglich sein sollten. Natürlich kann Dependency Injection - was bedeutet, dass ich eine Instanz einer Factory über den Konstruktor übergebe - den Trick machen (und tut es vorläufig). Dieser Ansatz scheitert jedoch, wenn ich noch flexibler werden und eine zweite Ebene dynamischer Objekterstellung einführen möchte. Bedeutung: (siehe oben) in dynamisch erstellten Objekte I-Objekte erstellen, um dynamicly wollen (sagen Implementierungen von einer abstrakten Basisklasse IBar - BarOne und BarTwo - wieder über eine Fabrik BarFactory).

Lassen Sie uns sagen BarOne erfordert eine IFoo Objekt aber BarTwo nicht. Ich habe noch die FooFactory zum BarFactory in jedem Fall zur Verfügung zu stellen, da eine der IBar Implementierungen könnte es brauchen. Wenn global zugängliche Fabriken zur Verfügung stehen, würde dieses Problem gemildert, und ich wäre nicht gezwungen, vorherzusagen, welche Fabriken durch Implementierungen einer spezifischen Schnittstelle benötigt werden. Außerdem könnte ich die Erstellungsmethoden direkt in der Quelldatei der Implementierungen registrieren.

FooFactory::Instance().registerCreator("new_creator", boost::bind(...)); 

Da ich denke, es ist eine gute Idee, was wäre der richtige Weg, um es zu implementieren?Ich war für einen Vorlagenansatz wie die SingletonHolder von Moderne C++ Design (siehe auch Loki Bibliothek), um die Fabriken zu wickeln. Allerdings würde ich es lieber als Meyers Singleton implementieren. Aber ich denke immer noch, dass es Probleme mit gemeinsam genutzten Bibliotheken geben wird. Die Lösung sollte mit GCC (und vorzugsweise MSVC) arbeiten. Ich bin auch offen für andere Ideen aus einem Design-Sicht, aber bitte vermeiden Sie die üblichen "Singletons sind böse" -rants. ;-)

Vielen Dank im Voraus.

+7

Singletons sind böse. ;-) Ich weiß, dass du das nicht hören willst, aber jetzt können Leute, die es sagen wollen, nur für diesen Kommentar stimmen! –

+0

@JohnZwinck: Danke für die Speicherung der Tastenanschläge! –

+0

"zweite Schicht der dynamischen Objekterstellung", wenn Sie eine 'TrucBiduleFactoryFactory' haben, ist Ihr Design saugt. –

Antwort

0

Hoffentlich, 76 Zeilen Code sprechen mehr als ein paar Worte - (C++ 11-Version dieser Funktionen anstelle der Boost diejenigen verwenden, aber sie sind so ziemlich das gleiche sowieso)

Ich würde setzen die Definition der Fabrik (en) und die Definition der Ersteller im gleichen (oder in der Nähe) Bereich, so dass jeder der Schöpfer "sehen" kann jede ihrer abhängigen Fabriken - Vermeidung der Notwendigkeit, Fabriken herumreichen zu viel und Vermeidung von Singletons

Autos & Sirenen:

class ISiren {}; 
class Siren : public ISiren 
{ 
public: 
    Siren() { std::cout << "Siren Created" << std::endl; } 
}; 

class ICar{}; 
class EstateCar : public ICar 
{ 
public: 
    EstateCar() { std::cout << "EstateCar created" << std::endl;} 
}; 
class PoliceCar : public ICar 
{ 
    std::shared_ptr<ISiren> siren; 
public: 
    PoliceCar(std::shared_ptr<ISiren> siren) 
     : siren(siren) 
    { 
     std::cout << "PoliceCar created" << std::endl; 
    } 
}; 

Fabriken:

typedef std::function< std::shared_ptr<ICar>() > CreatorType; 

class CarFactory 
{ 
    std::map<std::string, CreatorType> creators; 
public: 
    void AddFactory(std::string type, CreatorType func) 
    { 
     creators.insert(std::make_pair(type, func)); 
    } 

    std::shared_ptr<ICar> CreateCar(std::string type) 
    { 
     CreatorType& create(creators[type]); 
     return create(); 
    } 
}; 

class SirenFactory 
{ 
public: // Simple factory creating 1 siren type just for brevity 
    std::shared_ptr<ISiren> CreateSiren() { return std::make_shared<Siren>(); } 
}; 

"Fabrik Root" (Hauptfunktion, wo Fabriken definiert sind):

int main() 
{ 
    CarFactory car_factory; // Car factory unaware of Siren factory 
    SirenFactory siren_factory; 

    auto EstateCarLambda = []() { 
     return std::make_shared<EstateCar>(); 
    }; // Estate car lambda knows nothing of the Siren Factory 

    auto PoliceCarLambda = [&siren_factory]() { 
     return std::make_shared<PoliceCar>(siren_factory.CreateSiren()); 
    }; // Police car creation lambda using the Siren Factory 

    car_factory.AddFactory("EstateCar", EstateCarLambda); 

    car_factory.AddFactory("PoliceCar", PoliceCarLambda); 

    std::shared_ptr<ICar> car1 = car_factory.CreateCar("EstateCar"); 
    std::shared_ptr<ICar> car2 = car_factory.CreateCar("PoliceCar"); 
}