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的信用。