※デザパタのAbstractFactoryと紛らわしいけど違うっぽい...でもGoFのアレは「パターン」と呼べるのか?
...ともかく、抽象クラスのファクトリメソッドを呼んだら適切な具象クラスのインスタンスがnewされて返ってきたら便利だと思わないか?
だが親クラスは子クラスの実装は知らないのが原則(解放/閉鎖)なので普通にC++だけで組むと子クラスのファクトリメソッドを呼べなくて実装不可能...だと思ってたんだけど、ふと思いついてそれを可能にするテンプレートクラスを作ってみた。
template<typename T> class FACTORY{
FACTORY();
FACTORY(const FACTORY&);
void operator=(const FACTORY&);
public:
FACTORY(T* (*f)()) :
next(start),
factory(f){
start = this;
};
static T* New(){
for(FACTORY* f(start); f; f = (*f).next){
T* const t(((*f).factory)());
if(t){
return t;
}
}
return 0;
};
private:
static FACTORY* start;
FACTORY* const next;
T* (*const factory)();;
};
こんな感じ。
FACTORY<親クラス>で使う。
親クラス側で、
FACTORY<親クラス>::start(0);
をstaticに作っておいて子クラス側で、
FACTORY<親クラス> hoge(子クラスのファクトリメソッド)
とこれもstaticにインスタンスを作る。
するとstartはただの値なのでメモリに読み込んだ時点で初期化されてて、その後グローバルコンストラクタがファクトリメソッドを持ってるインスタンスを登録してくれるという寸法。あとは親クラスのファクトリの中で、
FACTORY<親クラス>::New();
するようにしとくと登録されたファクトリメソッドを順繰りに(でも指定はできない)呼び出してどれかが0以外の値を返したらその値が、でなければ0が帰ってくる。
当然子クラスのファクトリメソッドは自分がnewされるべきかどうか自分で調査しなきゃならない。例えば所定のデバイスファイルが存在するかどうかを調べるようにするとインターフェイスはそのままで、つながってるデバイスに合わせて適切なクラスを選択できる。もともとはOculusRiftのDK1とDK2を区別するために作ったものだし。
それと、今のところ子クラスのファクトリメソッドは「親クラス* (*)()」のみだけど、ファクトリメソッドをテンプレートで受けてうまく作ればもうちょっと色々できるかも。あと、グローバルコンストラクタを使ってるのでmainに入る前の動作は保証できない。
そうそう、このコード自体のライセンスはGPL3ね。GPLは著作権なのでGPLが嫌なら書き直してねー。