以下は、一連のジェネレーターを実行時に動的に登録する CRTP を使用した例です (c-startup)。シングルトン ファクトリ パターンは、実装で定義された選択されたアルゴリズムに従って、実行時にデータの新しいインスタンスを動的に作成するために使用されます。
設計は 2 つの部分 (namespace util) に分割されます。最初の部分は、PseudoRandomGenerator Base と Template クラス、およびすべてのジェネレーターの Factory で構成されます。2 番目の部分は、さまざまなデータ生成アルゴリズムを実装するためのものです。2 番目の部分の各実装クラスは、個別のファイル (この場合は 3 つ) に分割できます。
実施例1
#include <iostream>
#include <string>
#include <map>
#include <memory>
namespace PseudoRandomGeneratorTypes { enum : int { Type1, Type2, Type3 }; }
namespace util {
template<size_t SIZE>
struct __Data {
int a;
};
using Data = __Data<10>;
class PseudoRandomGenerator {
protected:
PseudoRandomGenerator() {}
public:
auto getType() const { return _type; }
virtual Data generate() const = 0;
protected:
int _type;
};
template<int PRGType, typename PRGImpl>
class PRGTmpl : public PseudoRandomGenerator {
public:
static PseudoRandomGenerator* CreatePtr() {
return new PRGImpl();
}
static const int TYPE;
protected:
PRGTmpl() { _type = TYPE; }
};
class PseudoRandomGeneratorFactory {
public:
typedef PseudoRandomGenerator* (*psg)();
static auto get()
{
static PseudoRandomGeneratorFactory fact;
return &fact;
}
auto Register(int id, psg m)
{
_map[id] = m;
return id;
}
auto Create(int id)
{
return _map[id]();
}
private:
PseudoRandomGeneratorFactory() {}
~PseudoRandomGeneratorFactory() {}
std::map<int, psg> _map;
};
template <int arbitaryPRGType, typename arbitaryPRGImpl>
const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::CreatePtr);
}
namespace util {
class PRGType1 : public PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 > {
public:
virtual Data generate() const override final { return Data{ 111 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 >;
class PRGType2: public PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 > {
public:
virtual Data generate() const override final { return Data{ 222 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 >;
class PRGType3 : public PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 > {
public:
virtual Data generate() const override final { return Data{ 333 }; }
};
template class PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 >;
}
using namespace util;
using namespace std;
int main()
{
auto rng1 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1));
auto rng2 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2));
auto rng3 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3));
cout << rng1->generate().a << endl;
cout << rng2->generate().a << endl;
cout << rng3->generate().a << endl;
}
さらに、一度に 1 つのインスタンスのみが必要で、ヒープを使用したくない場合は、CreatePtr() 関数を使用します。以下に置き換えることができます。
static PseudoRandomGenerator* GetPtr() {
static PRGImpl _m;
_m = PRGImpl();
return &_m;
}
スペシャライゼーションは次のように変更されます。
template <int arbitaryPRGType, typename arbitaryPRGImpl>
const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::GetPtr);
使用パターンは次のように変更されます。
auto rng1 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1);
auto rng2 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2);
auto rng3 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3);
2 番目の例では、マップの代わりに単純な C スタイルの配列を使用して、動的メモリ割り当てを回避できます。
実施例2
C++ テンプレートを使用して、コンパイル時に AbstractFactory にコンストラクター メソッドを動的に登録するから適応