2

私はしばらくウェブを読んだり検索したりしてきましたが、良い解決策が見つかりませんでした。これが私がやりたいことです:

抽象基本クラスを定義するライブラリを作成しています - それをIFooと呼びましょう。

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

ライブラリは、そのためのいくつかの実装も定義しています - それらをFooLibOneFooLibTwoと呼びましょう。

作成プロセスをカプセル化し、ランタイム パラメータに応じてどの具体的な実装を使用するかを決定するために、std::string をファクトリ メソッドにマップするファクトリFooFactoryを使用します (私の場合は boost::function ですが、それは重要ではありません)。ここ)。また、新しいファクトリ メソッドを登録することもできます。次のようになります。

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

今のところ、ライブラリによって提供される実装 (FooLibOne、FooLibTwo) をFooFactoryのコンストラクターに直接追加しました。したがって、それらは常に利用可能です。一部のライブラリ コードは特定のオブジェクトなどを初期化するために FooFactory を使用します。ティー パターンは頻繁に議論され、Singleton パターンのさまざまな実装がどのように機能するか確信が持てなかったため、これまでのところ、ファクトリに Singleton パターンを使用することは控えてきました。おそらく複数の共有ライブラリなどと組み合わせて。

ただし、工場を回るのは少し面倒な場合があり、これは Singleton パターンが役立つ機会の 1 つだと思います。特に、ライブラリのユーザーは、(既存の) ライブラリコードからもアクセスできるIFooの実装をさらに追加できる必要があると考えています。もちろん、依存性注入 (つまり、コンストラクターを介してファクトリのインスタンスを渡すことを意味します) は、そのトリックを行うことができます (そして、今のところ実行しています)。しかし、さらに柔軟になり、動的オブジェクト作成の第 2 層を導入したい場合、このアプローチは失敗します。意味: 動的に作成されたオブジェクト (抽象基本クラスIBarの実装- BarOneおよびBarTwo - 再びファクトリBarFactory経由)。

BarOneにはIFooオブジェクトが必要ですが、 BarTwoには必要ないとしましょIBar実装の 1 つがそれを必要とする可能性があるため、いずれにせよ、BarFactory にFooFactory提供する必要があります。グローバルにアクセス可能なファクトリがあれば、この問題は軽減され、特定のインターフェイスの実装でどのファクトリが必要になるかを予測する必要はありません。さらに、作成メソッドを実装のソース ファイルに直接登録することもできました。

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

それは良い考えだと思うので、それを実装する正しい方法は何でしょうか? ファクトリをラップするために、Modern C++ Design ( Lokiライブラリも参照) のSingletonHolderのようなテンプレート化されたアプローチを使用していました。ただし、代わりにマイヤーのシングルトンとして実装したいと思います。しかし、共有ライブラリにはまだ問題があると思います。このソリューションは、GCC (およびできれば MSVC) で動作するはずです。私はデザインの観点から他のアイデアにもオープンですが、よくある「シングルトンは悪だ」という暴言は避けてください。;-)

前もって感謝します。

4

1 に答える 1

0

願わくば、76 行のコードがいくつかの単語よりも多くを話します - (boost 機能の代わりにこれらの機能の C++11 バージョンを使用しますが、とにかくほとんど同じです)

ファクトリーの定義とクリエーターの定義を同じ (または近くの) スコープに配置して、各クリエーターが依存するファクトリーを「見る」ことができるようにします。多く、シングルトンを避ける

車とサイレン:

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

工場:

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

"Factory Root" (ファクトリが定義されているメイン関数) :

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" );
}
于 2012-04-01T20:58:49.730 に答える