如何不使用switch随机选择要实例化的类?

时间:2021-07-20 14:59:47

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:

我重构了一个3000+-line类,它带有一个复杂的条件网络,并切换成一组工人类。之前,构造函数的一部分将通过如下代码选择要使用的对象的“类型”:

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:

重构之后,我有了单独的TypeFoo、TypeBar和TypeBaz类,每个类都有自己的Run()方法来完成它们的工作。遗憾的是,类选择代码很复杂。我不知道有什么方法可以保留一列可能要构建的类,所以我有:

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?

是否有相对简单的方法来实现原始代码的清晰性?

2 个解决方案

#1


14  

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):

然后将其用作(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;
}   

#2


2  

I would suggest creating a common base class (if you've not already got one) and then using a factory class to encapsulate the creation process. The factory would just return a pointer to your base class which has the prototype run method.

我建议创建一个公共基类(如果您还没有的话),然后使用一个工厂类来封装创建过程。工厂只需返回一个指向基类的指针,该基类具有prototype run方法。

Something along these lines:

沿着这些线路:

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

So to use it you can just call:

所以要使用它,你可以直接调用:

Type *t = TypeFactory::RandomTypeFooWeighted();

Credit to Nawaz for the function pointer bits and bobs.

为函数指针位和bobs提供给Nawaz的信用。

#1


14  

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):

然后将其用作(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;
}   

#2


2  

I would suggest creating a common base class (if you've not already got one) and then using a factory class to encapsulate the creation process. The factory would just return a pointer to your base class which has the prototype run method.

我建议创建一个公共基类(如果您还没有的话),然后使用一个工厂类来封装创建过程。工厂只需返回一个指向基类的指针,该基类具有prototype run方法。

Something along these lines:

沿着这些线路:

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

So to use it you can just call:

所以要使用它,你可以直接调用:

Type *t = TypeFactory::RandomTypeFooWeighted();

Credit to Nawaz for the function pointer bits and bobs.

为函数指针位和bobs提供给Nawaz的信用。