2

我在我的 C++ 项目中应用了工厂设计模式,您可以在下面看到我是如何做到的。我尝试通过遵循“反 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 的情况下创建我的对象。(我知道我有点偏执,但我想知道是否可以做到。)

4

8 回答 8

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

请注意,不需要复制构造函数,如您的示例所示。

于 2010-08-17T11:43:05.593 回答
5

工厂的重点不是摆脱 ifs,而是将它们放在真实业务逻辑代码的单独位置,而不是污染它。这只是关注点的分离。

于 2010-08-17T11:33:53.633 回答
2

如果你很绝望,你可以编写一个跳转表/克隆()组合,它可以在没有 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;
    }
};

但是,只有当您拥有大量不同的芯片类型时,这种方法才有价值。对于少数人来说,写几个 if 会更有用。

快速说明:我使用了 std::unordered_map 和 std::unique_ptr,它们可能不是您的 STL 的一部分,这取决于您的编译器有多新。替换为 std::map/boost::unordered_map 和 std::/boost::shared_ptr。

于 2010-08-17T11:45:26.607 回答
1

不,你不能摆脱如果。createChip 方法根据您作为参数传递的常量(类型名称)创建一个新实例。但是您可以优化您的代码,从 if 语句中删除这 2 行。

 microcontrollers[type] = newController;
 return microcontrollers[type];
于 2010-08-17T11:39:22.487 回答
1

要回答您的问题:是的,您应该创建一个带有映射到构造所需对象的函数的工厂。构造的对象应该提供并向工厂本身注册该功能。

在其他几个 SO 问题中也有一些关于这个主题的阅读,所以我会让你阅读而不是在这里解释它。

C++ 中的泛型工厂

有没有办法从保存类名的字符串中实例化对象?

于 2010-08-17T12:15:53.660 回答
0

您可以if在工厂中使用 s - 只是不要让它们在您的代码中乱扔垃圾。

于 2010-08-17T11:30:38.827 回答
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();
}
于 2010-08-17T11:38:41.353 回答
0

本质上,您所要求的称为Virtual Construction,即构建仅在运行时才知道其类型的对象的能力。

当然 C++ 不允许构造函数是虚拟的,所以这需要一些技巧。常见的 OO 方法是使用以下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 习语。

于 2010-08-17T12:19:34.543 回答