Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

c++ - How to randomly select a class to instantiate without using switch?

I'm refactoring a single 3000+-line class with a tangled web of conditionals and switches into a set of worker classes. Previously part of the constructor would select which "type" of thing to use via code like the following:

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

After refactoring I have separate TypeFoo, TypeBar and TypeBaz classes that each have their own Run() methods to do their job. Sadly, its complicated the class selection code. I don't know of any way to keep a list of possible classes to construct, so I have this:

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

This is still worth the change because this initialisation code is not called regularly, but its now harder to modify this list, change weightings, etc.

Is there a relatively straightforward to achieve the clarity of the original code?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The answer is : a base class and an array of function pointers can help you do that.

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
}

Then use it as (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;
}   

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...