C++ 工厂模式实现 - 示例、问题和关注点

cda*_*hms 7 c++ factory-pattern

最近我一直在尝试更好地理解在 C++ 中使用工厂模式。

我查阅了以下资源:

如何在C++中正确实现工厂方法模式

https://www.geeksforgeeks.org/design-patterns-set-2-factory-method/

https://sourcemaking.com/design_patterns/factory_method/cpp/1

https://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus

https://www.bogotobogo.com/DesignPatterns/factorymethod.php

https://gist.github.com/pazdera/1099562

https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns

在有人将其标记为“不是问题”之前,我确实有一些明确的问题,我稍后会讨论。但为了首先澄清我到目前为止的理解,我想从一个例子开始。这是我从上述链接中的信息中得到的最好的例子(最好的含义说明了工厂模式,但尽可能简单,因此该模式的初学者可以清楚地看到所涉及的问题):

// FactoryPattern.cpp

#include <iostream>
#include <vector>

enum AnimalSpecies { dog, cat };


class Animal
{
public:
  virtual void makeSound() = 0;
};

class Dog : public Animal
{
public:
  void makeSound() { std::cout << "woof" << "\n\n"; }
};

class Cat : public Animal
{
public:
  void makeSound() { std::cout << "meow" << "\n\n"; }
};

class AnimalFactory
{
public:
  static Animal* makeAnimal(AnimalSpecies animalSpecies);
};

Animal* AnimalFactory::makeAnimal(AnimalSpecies animalSpecies)
{
  if (animalSpecies == AnimalSpecies::dog)
  {
    return(new Dog());
  }
  else if (animalSpecies == AnimalSpecies::cat)
  {
    return(new Cat());
  }
  else
  {
    std::cout << "error in AnimalFactory::makeAnimal(), animalSpecies = " << animalSpecies << " does not seem to be valid" << "\n\n";
    return(nullptr);
  }
}

int main(void)
{
  std::vector<Animal*> animals;

  animals.push_back(AnimalFactory::makeAnimal(AnimalSpecies::dog));
  animals.push_back(AnimalFactory::makeAnimal(AnimalSpecies::cat));

  for (auto &animal : animals)
  {
    animal->makeSound();
  }

  for (auto& animal : animals)
  {
    delete(animal);
  }

  return(0);
}
Run Code Online (Sandbox Code Playgroud)

该程序的输出(按预期)是:

woof

meow
Run Code Online (Sandbox Code Playgroud)

这是我此时的问题:

1)我对工厂模式所做的一些阅读使我相信消除 if 语句是好处的一部分。上面的例子在这方面失败了,因为必须使用 in 来确定物种AnimalFactory::makeAnimal()if-else有没有办法重构它以删除 if 语句?

2) 随着最近 C++ 14/17/20 的各种变化和改进,我们中的许多人正在远离指针以避免内存泄漏的可能性。有没有办法重构它以便不使用指针?

3)工厂模式的优点是什么?即在上面的示例中为什么不省略类AnimalFactory并简单地进行main()如下操作:

int main(void)
{
  Dog dog;
  dog.makeSound();

  Cat cat;
  cat.makeSound();

  return(0);
}
Run Code Online (Sandbox Code Playgroud)

Lit*_*les 2

回复 1+3:您可以通过将 if-else 替换为 switch-case 语句(等效,但更明确)或创建从枚举类型到指向构造函数的指针的静态映射来删除 if-else。然而:工厂模式并不是要消除所有的 if 语句;而是要消除所有的 if 语句。它只是将创建逻辑封装在一个地方,因此每次您需要从枚举类型创建新动物时都不会重复。

使用工厂的优点是,当逻辑不像构造函数调用那么简单时,可以隐藏从类层次结构中创建对象的复杂性。例如:

  1. 涉及到很多设置,因此您希望创建一个对象,该对象创建具有相同设置的所有对象;例如,一个动物工厂创建了所有动物,并将标志is_noisy设置为 false。
  2. 对象创建逻辑并不简单,例如从 JSON 格式的配置字符串创建动物。
  3. 还有其他全局方面,例如计算所有分配的动物。