【问题标题】:Factory method anti-if implementation工厂方法反if实现
【发布时间】:2010-08-17 11:29:17
【问题描述】:

我在我的 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 的情况下创建我的对象。 (我知道我有点偏执,但我想知道它是否可以做到。)

【问题讨论】:

  • 至少 GOF 没有工厂模式。有工厂方法或抽象工厂。这对我来说似乎是一种工厂方法。
  • @chubsdad 他在标题中写了Factory Method。
  • 哦,我没仔细注意,谢谢

标签: c++ factory-method


【解决方案1】:

你是对的,你应该使用从键到创建函数的映射。 在你的情况下,它会是

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

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

【讨论】:

  • 我无法按照您的示例进行操作。函数指针映射在哪里使用?还有为什么不需要copy ctor?
  • 映射用于 createChip() 函数,以检查我们是否有给定字符串的创建函数。如果是这样,我们只需调用该函数,它返回一个指向新对象的指针。由于我们返回这个指针,我们不需要复制对象。
  • 谢谢,但是你如何将CreateInstance 功能放入地图中?
  • 好的,我知道了creators["R500"] = ChipR500::createInstance;
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    如果你很绝望,你可以编写一个跳转表/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;
        }
    };
    

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

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

    【讨论】:

      【解决方案4】:

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

       microcontrollers[type] = newController;
       return microcontrollers[type];
      

      【讨论】:

      • 事实上,你可以只返回微控制器[type] = newController,因为 operator= 返回 T&。
      【解决方案5】:

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

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

      Generic factory in C++

      Is there a way to instantiate objects from a string holding their class name?

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:
          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();
          }
          

          【讨论】:

            【解决方案8】:

            本质上,您所要求的称为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&lt;std::string,Chip*&gt;)。通常,地图被实例化为单例。

            另一种方法,正如到目前为止所说明的那样,是类似的,包括直接注册方法而不是对象。这可能是也可能不是您个人的偏好,但它通常会稍微快一些(不多,您只是避免虚拟调度)并且内存更容易处理(您不必对函数指针执行delete)。

            然而,您应该注意的是内存管理方面。您不想泄露信息,因此请务必使用 RAII 习语。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-07-27
              相关资源
              最近更新 更多