工厂方法反执行

时间:2010-08-17 11:29:18

标签: c++ factory-method

我正在我的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。 (我知道我有点偏执,但我想知道它是否可以完成。)

8 个答案:

答案 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问题上也有一些关于这个问题的阅读,所以我会让你读到这些,而不是在这里解释。

Generic factory in C++

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习语。