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

时间:2019-06-02 13:21:52

标签: c++ factory-pattern

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

我已经咨询了以下资源:

How to implement the factory method pattern in C++ correctly

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

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

woof

meow

这是我目前的问题:

1)我对工厂模式所做的一些阅读使我相信消除if语句是其中的一部分。上面的示例在这方面失败了,因为在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);
}

2 个答案:

答案 0 :(得分:3)

  

使我相信消除if语句是其中的一部分。

恕我直言,工厂模式的真正动力来自虚拟化工厂模式:

class AnimalFactoryInterface
{
  public:
    virtual Animal* makeAnimal() = 0;
};

class CatFactory : public AnimalFactoryInterface
{
  public:
    Animal* makeAnimal() override { return new Cat(); }
};

这虚拟化了如何创建新动物,否则就无法做到。

switch语句构造函数对于从配置文件中创建动物非常有用,但这实际上并不是功能所在。在创建接受如何制作通用动物的通用Breeder类时,它会有所帮助。

  

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

返回智能指针(std::unique_ptr<Animal>)。

  

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

首先,它很容易实现

class Breeder {
 public:
   Breeder(std::unique_ptr<AnimalFactoryInterface> animal_factory);
   void Breed() { animals_.push_back(animal_factory_->Create()); }
   // ... more stuff
 private:
   std::unique_ptr<AnimalFactoryInterface> animal_factory_;
   std::vector<std::unique_ptr<Animal>> animals_;
};

第二个是Dependency Injection

我们还应该阐明“工厂模式”。

我在此答案中描述的是Abstract Factory Pattern-即利用工厂接口来创建对象。

Factory Method Pattern是使用带有switch语句的单个方法来创建对象的方法。它肯定有它的实用程序,但是我总是发现它有点明显,以至于不能过多地将其作为完整的“设计模式”。但是对于这个问题以及对优势问题而言,最重要的是,如果您所做的只是简单地制造一系列对象,那么您是对的-您绝对不需要工厂类,甚至不需要如果您要做的只是创建一次类,则需要一个特殊的方法。任何编程的经验法则都是最简单的方法。

答案 1 :(得分:1)

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

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

  1. 涉及许多设置,因此您要创建一个对象,该对象将使用相同的设置创建所有对象。例如一个动物工厂,该工厂创建的所有动物的标志is_noisy都设置为false。
  2. 对象创建逻辑并非无关紧要,例如通过JSON格式的配置字符串创建动物。
  3. 还有其他全局方面,例如计算所有分配的动物。