我正在我的C ++项目中应用Factory设计模式,下面你可以看到我是如何做到的。我尝试通过遵循“反if”活动来改进我的代码,因此想要删除我所拥有的if语句。知道我该怎么办?
typedef std::map<std::string, Chip*> ChipList;
Chip* ChipFactory::createChip(const std::string& type) {
MCList::iterator existing = Chips.find(type);
if (existing != Chips.end()) {
return (existing->second);
}
if (type == "R500") {
return Chips[type] = new ChipR500();
}
if (type == "PIC32F42") {
return Chips[type] = new ChipPIC32F42();
}
if (type == "34HC22") {
return Chips[type] = new Chip34HC22();
}
return 0;
}
我会想象创建一个以字符串为键的映射,以及构造函数(或创建对象的东西)。之后,我可以使用类型(类型是字符串)从地图中获取构造函数并创建我的对象而不使用任何if。 (我知道我有点偏执,但我想知道它是否可以完成。)
答案 0 :(得分:7)
你是对的,你应该使用从键到创建功能的映射。 在你的情况下,它将是
typedef Chip* tCreationFunc();
std::map<std::string, tCreationFunc*> microcontrollers;
为每个新的芯片驱动类ChipXXX添加静态功能:
static Chip* CreateInstance()
{
return new ChipXXX();
}
并将此功能注册到地图中。
你的工厂功能应该是这样的想法:
Chip* ChipFactory::createChip(std::string& type)
{
ChipList::iterator existing = microcontrollers.find(type);
if (existing != microcontrollers.end())
return existing->second();
return NULL;
}
请注意,不需要复制构造函数,如您的示例所示。
答案 1 :(得分:5)
工厂的要点不是摆脱ifs,而是将它们放在真实业务逻辑代码的单独位置而不是污染它。这只是一个关注点的分离。
答案 2 :(得分:2)
如果你很绝望,你可以编写一个跳转表/ clone()组合来完成这项工作而不使用if语句。
class Factory {
struct ChipFunctorBase {
virtual Chip* Create();
};
template<typename T> struct CreateChipFunctor : ChipFunctorBase {
Chip* Create() { return new T; }
};
std::unordered_map<std::string, std::unique_ptr<ChipFunctorBase>> jumptable;
Factory() {
jumptable["R500"] = new CreateChipFunctor<ChipR500>();
jumptable["PIC32F42"] = new CreateChipFunctor<ChipPIC32F42>();
jumptable["34HC22"] = new CreateChipFunctor<Chip34HC22>();
}
Chip* CreateNewChip(const std::string& type) {
if(jumptable[type].get())
return jumptable[type]->Create();
else
return null;
}
};
但是,当您拥有大量不同的芯片类型时,这种方法才会变得有价值。对于少数几个,只写一些ifs更有用。
快速注意:我使用了std :: unordered_map和std :: unique_ptr,它们可能不是STL的一部分,具体取决于编译器的新增功能。替换为std :: map / boost :: unordered_map和std :: / boost :: shared_ptr。
答案 3 :(得分:1)
不,你不能摆脱ifs。 createChip方法根据您作为参数传递的常量(类型名称)创建一个新实例。 但你可以稍微调整一下你的代码,如果你的判断有一点那么删除那两行。
microcontrollers[type] = newController;
return microcontrollers[type];
答案 4 :(得分:1)
回答你的问题:是的,你应该建立一个带有地图的工厂来构建你想要的对象的函数。构造的对象应该自己向工厂提供和注册该功能。
在其他几个SO问题上也有一些关于这个问题的阅读,所以我会让你读到这些,而不是在这里解释。
Is there a way to instantiate objects from a string holding their class name?
答案 5 :(得分:0)
您可以在工厂中拥有if
- 只需在代码中不要让它们散落。
答案 6 :(得分:0)
struct Chip{
};
struct ChipR500 : Chip{};
struct PIC32F42 : Chip{};
struct ChipCreator{
virtual Chip *make() = 0;
};
struct ChipR500Creator : ChipCreator{
Chip *make(){return new ChipR500();}
};
struct PIC32F42Creator : ChipCreator{
Chip *make(){return new PIC32F42();}
};
int main(){
ChipR500Creator m; // client code knows only the factory method interface, not the actuall concrete products
Chip *p = m.make();
}
答案 7 :(得分:0)
基本上,你要求的是Virtual Construction
,即构建一个只在运行时才知道其类型的对象的能力。
当然C ++不允许构造函数是虚拟的,所以这需要一些技巧。常见的面向对象方法是使用Prototype
模式:
class Chip
{
public:
virtual Chip* clone() const = 0;
};
class ChipA: public Chip
{
public:
virtual ChipA* clone() const { return new ChipA(*this); }
};
然后实例化这些原型的地图并使用它来构建对象(std::map<std::string,Chip*>
)。通常,地图被实例化为单身。
到目前为止,已经说明的另一种方法是类似的,包括直接注册方法而不是对象。它可能或可能不是您的个人偏好,但它通常稍快(不多,您只是避免虚拟调度)并且内存更容易处理(您不必在指向函数的指针上执行delete
)。
然而,你应该注意的是内存管理方面。你不想泄漏,所以一定要使用RAII习语。