2011-10-18 12 views
7

私は、コンディショナルのもつれたウェブで1つの3000 +ラインクラスをリファクタリングしており、一連のワーカークラスに切り替わります。コンストラクタの以前の部分は、次のようなコードを経由して使用する事のある「タイプ」を選択します:スイッチを使用せずにインスタンス化するクラスをランダムに選択する方法はありますか?

enum Type { FOO, BAR, BAZ }; 

Type choices[] = { FOO, FOO, BAR, BAZ }; // weighted towards FOO 
m_type = choices[rand()%4]; 

[...later...] 

void Run() { 
    switch (m_type) { 
     case FOO: do_foo(); break; 
     case BAR: do_bar(); break; 
     case BAZ: do_baz(); break; 
    } 
} 

リファクタリングした後、私は、それぞれが自分の仕事をするために、独自のRun()のメソッドを持っている別のTypeFooTypeBarTypeBazクラスを持っています。悲しいことに、そのクラス選択コードは複雑です。私が構築することが可能なクラスのリストを維持するためにどのような方法を知らないので、私はこれを持っている:

Type *m_type; 

switch (mrand()%4) { 
    case 0: case 1: m_type = new TypeFoo(); break; 
    case 1:   m_type = new TypeBar(); break; 
    case 2:   m_type = new TypeBaz(); break; 
} 

これは、この初期化コードは定期的に呼ばれていないため、まだ変更の価値があるが、それにその今困難このリストを変更したり、重みを変更したりすることができます。

元のコードの明瞭さを達成するのは比較的簡単ですか?

+1

1:すべてのこれらのクラスは、単一の基本クラスから継承してください:-)興味深い質問を...? –

+0

これはやや良いでしょう: 'switch(mrand()%4){ case BAR:t = new TypeBar();ブレーク; ケースBAZ:t =新しいTypeBaz();ブレーク; デフォルト:t =新しいTypeFoo();ブレーク; // Fooに重み付けされました } ' – Yourpalal

+0

実行時に選択が行われる必要がある場合、なぜ' template'タグが付けられますか? – iammilind

答えて

14

答えは:基本クラスと関数ポインタの配列がそうするのを助けることができます。

struct Base { virtual ~Base() {} }; //make ~Base() virtual 
struct Foo : Base {}; 
struct Bar : Base {}; 
struct Baz : Base {}; 

template<typename T> 
Base *Create() { return new T(); } 

typedef Base* (*CreateFn)(); 

CreateFn create[] = 
     { 
       &Create<Foo>, 
       &Create<Foo>, // weighted towards FOO 
       &Create<Bar>, 
       &Create<Baz> 
     }; 
const size_t fncount = sizeof(create)/sizeof(*create); 

Base *Create() 
{ 
    return create[rand() % fncount](); //forward the call 
} 

その後(ideone demo)としてそれを使用する:

int main() { 
     Base *obj = Create(); 
     //work with obj using the common interface in Base 

     delete obj; //ok, 
        //the virtual ~Base() lets you do it 
        //in a well-defined way 
     return 0; 
} 
+0

おそらく、基本クラスポインタを介して派生クラスオブジェクトを保持しているので、Baseに仮想デストラクタを追加したいと思うかもしれません。 –

+0

+1:それは私が気にしていたものです。あなたはコード例でそれに私を打ち負かす:-) –

+0

@エリズ:私はずっと前にそれをやった。 – Nawaz

2

私は共通の基底クラスを作成しておいてから、a factory classを使って作成プロセスをカプセル化することをお勧めします。ファクトリはプロトタイプ実行メソッドを持つ基本クラスへのポインタを返します。これらの線に沿って

何か:

Type *t = TypeFactory::RandomTypeFooWeighted(); 

クレジットナワズに関数ポインタの小物類のために:

class Type 
{ 
    virtual void Run() = 0; 
}; 

class TypeFoo : public Type 
{ 
public: 
    TypeFoo() {}; 
    virtual void Run() {}; 
    static Type* Create() { return new TypeFoo(); }; 
}; 

class TypeBar : public Type 
{ 
public: 
    TypeBar() {}; 
    virtual void Run() {}; 
    static Type* Create() { return new TypeBar(); }; 
}; 

class TypeBaz : public Type 
{ 
public: 
    TypeBaz() {}; 
    virtual void Run() {}; 
    static Type* Create() { return new TypeBaz(); }; 
}; 

class TypeFactory 
{ 
    typedef Type* (*CreateFn)(); 

public: 
    static Type* RandomTypeFooWeighted() 
    { 
     CreateFn create[] = 
     { 
      TypeFoo::Create, 
      TypeFoo::Create, // weighted towards FOO 
      TypeBar::Create, 
      TypeBaz::Create 
     }; 
     const int fncount = sizeof(create)/sizeof(*create); 
     return create[ rand()%fncount ](); 
    } 
}; 

だから、あなただけ呼び出すことができ、それを使用します。

関連する問題