• (四)详解工厂模式


    一.为什么需要工厂模式

    当我们在拥有了大量类的时候,一旦我们需要具体对象,就需要手动控制new出不同的对象。我们可以针对这一步骤抽象化,创建一个根据要求返回我们需要的具体对象的工厂。这样就能通过统一的方式获得不同的对象

    二.不用工厂模式

    假设我们拥有一个披萨店,我们需要根据客户不同的订单提供不同的披萨。首先不使用工厂模式,我们就能得到这样的代码

    1. #include
    2. #include
    3. class Pizza {
    4. public:
    5. virtual void prepare() = 0;
    6. virtual void bake() = 0;
    7. virtual void cut() = 0;
    8. virtual void box() = 0;
    9. };
    10. //不同种类的披萨
    11. class CheesePizza: public Pizza {
    12. void prepare()
    13. {
    14. std::cout << "准备芝士" << std::endl;
    15. };
    16. void bake()
    17. {
    18. std::cout << "烤芝士" << std::endl;
    19. };
    20. void cut()
    21. {
    22. std::cout << "切芝士" << std::endl;
    23. };
    24. void box()
    25. {
    26. std::cout << "装盒" << std::endl;
    27. };
    28. };
    29. class GreekPizza: public Pizza {
    30. void prepare()
    31. {
    32. std::cout << "。。。" << std::endl;
    33. };
    34. void bake()
    35. {
    36. std::cout << "。。。" << std::endl;
    37. };
    38. void cut()
    39. {
    40. std::cout << "。。。" << std::endl;
    41. };
    42. void box()
    43. {
    44. std::cout << "。。。" << std::endl;
    45. };
    46. };
    47. class PepperoniPizza: public Pizza {
    48. void prepare()
    49. {
    50. std::cout << "。。。" << std::endl;
    51. };
    52. void bake()
    53. {
    54. std::cout << "。。。" << std::endl;
    55. };
    56. void cut()
    57. {
    58. std::cout << "。。。" << std::endl;
    59. };
    60. void box()
    61. {
    62. std::cout << "。。。" << std::endl;
    63. };
    64. };
    65. std::unique_ptr orderPizza(std::string type)
    66. {
    67. std::unique_ptr pizza = nullptr;
    68. if (type == "cheese")
    69. {
    70. pizza = std::make_unique();
    71. }
    72. else if (type == "greek")
    73. {
    74. pizza = std::make_unique();
    75. }
    76. else if (type == "pepperoni")
    77. {
    78. pizza = std::make_unique();
    79. }
    80. pizza->prepare();
    81. pizza->bake();
    82. pizza->cut();
    83. pizza->box();
    84. return pizza;
    85. };

    三.使用简单工厂模式

    能看到假若我们想要对具体类型的披萨进行调整是很困难的。我们将工厂引入,单独用一个函数创建对象。就可以得到这样的代码。通过统一的createPizza函数获得不同的披萨对象

    1. #include "Pizza.hpp"
    2. #include
    3. class SimplePizzaFactory
    4. {
    5. public:
    6. std::unique_ptr createPizza(const std::string& type)
    7. {
    8. std::unique_ptr pizza = nullptr;
    9. if (type == "cheese")
    10. {
    11. pizza = std::make_unique();
    12. }
    13. else if (type == "pepperoni")
    14. {
    15. pizza = std::make_unique();
    16. }
    17. else if (type == "clam")
    18. {
    19. pizza = std::make_unique();
    20. }
    21. else if (type == "veggie")
    22. {
    23. pizza = std::make_unique();
    24. }
    25. return pizza;
    26. }
    27. };

    这样结合工厂模式就可以得到如下的披萨店的代码。

    1. #include "SimplePizzaFactory.hpp"
    2. #include "Pizza.hpp"
    3. #include
    4. class PizzaStore
    5. {
    6. SimplePizzaFactory factory;
    7. public:
    8. PizzaStore(SimplePizzaFactory factory)
    9. {
    10. this->factory = factory;
    11. }
    12. std::unique_ptr orderPizza(std::string type)
    13. {
    14. std::unique_ptr pizza = nullptr;
    15. pizza = factory.createPizza(type);
    16. if (pizza != nullptr)
    17. {
    18. pizza->prepare();
    19. pizza->bake();
    20. pizza->cut();
    21. pizza->box();
    22. }
    23. else
    24. {
    25. std::cout << "不是可提供的披萨" << std::endl;
    26. }
    27. return pizza;
    28. }
    29. };

    这样就可以通过字符串自动判断并生成对应的对象。也有利于我们增加新的披萨种类。但是对于更改已有披萨的设计仍然不是很方便。

    四.工厂模式

    我们想让每种不同的披萨对象拥有更多的弹性。我们可以将这一弹性功能下放到PizzaStore实现。也就是让子类运行时根据输入决定做什么披萨,怎么做披萨。我们需要提供多种不同类型的PizzaStore,并根据不同的方法创建不同的披萨类型。

    首先提供披萨店的接口和披萨的接口

    1. #include "Pizza.hpp"
    2. #include
    3. class PizzaStore
    4. {
    5. public:
    6. std::unique_ptr orderPizza(std::string type);
    7. virtual std::unique_ptr makePizza(std::string type) = 0;
    8. };
    1. class Pizza
    2. {
    3. public:
    4. std::string name;
    5. std::string dough;
    6. std::string sauce;
    7. std::vector toppings;
    8. const std::string& getName() const
    9. {
    10. return this->name;
    11. }
    12. virtual void prepare() const
    13. {
    14. std::cout << "准备 " + getName() << std::endl;
    15. std::cout << "准备面团..." << std::endl;
    16. std::cout << "添加酱汁..." << std::endl;
    17. std::cout << "添加配料: " << std::endl;
    18. for (auto& topping : this->toppings)
    19. {
    20. std::cout << " " + topping + "\n";
    21. }
    22. }
    23. virtual void bake() const
    24. {
    25. std::cout << "350 度 烤 25 分钟 "<< std::endl;
    26. }
    27. virtual void cut() const
    28. {
    29. std::cout << "切分披萨"<< std::endl;
    30. }
    31. virtual void box() const
    32. {
    33. std::cout << "披萨装盒" << std::endl;
    34. }
    35. virtual ~Pizza() = default;
    36. };

    有了披萨店的接口和披萨的接口,我们就能创建出不同种类的披萨商店贩卖不同种类的披萨。

    首先创建出不同类型的披萨。并将其装载在披萨店内。

    1. #include "Pizza.hpp"
    2. class NYStylePepperoniPizza: public Pizza
    3. {
    4. public:
    5. NYStylePepperoniPizza()
    6. {
    7. name = "NY style pepperoni pizza";
    8. dough = "。。。面";
    9. sauce = "。。。酱";
    10. toppings.push_back("。。。e");
    11. toppings.push_back("。。。");
    12. toppings.push_back("。。。");
    13. toppings.push_back("。。。");
    14. toppings.push_back("。。。");
    15. toppings.push_back("。。。");
    16. }
    17. };
    1. #include "Pizza.hpp"
    2. #include "PizzaStore.hpp"
    3. class NYPizzaStore: public PizzaStore
    4. {
    5. public:
    6. std::unique_ptr makePizza(std::string type)
    7. {
    8. std::unique_ptr pizza = nullptr;
    9. if (type == "cheese")
    10. {
    11. pizza = std::make_unique();
    12. }
    13. else if (type == "pepperoni")
    14. {
    15. pizza = std::make_unique();
    16. }
    17. return pizza;
    18. }
    19. };

    这样披萨店的工厂会拥有更好的弹性,能更利于增加新的披萨,由于每个商店都拥有一个特有的披萨类,也利于披萨类本身的更改。

    披萨本身和披萨店本身是平行的关系,披萨是产品类,披萨商店是创建者

    工厂模式定义:定义了一个创建对象的接口,但让子类决定实例化哪个类。

    本质就是工厂接口创建出工厂实例,调用产品接口实现出产品实例。

    五.工厂模式应有的对象依赖关系

    依赖抽象不依赖具体类,应该让依赖倒置。高层组件的类例如(pizzastore)依赖具体实现类例如(具体pizza类)。

    六.抽象工厂模式

    对于上面的工厂模式的披萨而言,原料都是固定好的,不利于修改,而抽象工厂模式利于提供大量家族相关对象,就比如上面的披萨原料。我们可以通过抽象工厂来改造披萨的原料。首先获得我们新的pizza虚接口,跟工厂模式没有什么变化。

    1. class Pizza
    2. {
    3. public:
    4. std::string name;
    5. Dough dough;
    6. Sauce sauce;
    7. std::vector toppings;
    8. const std::string& getName() const
    9. {
    10. return this->name;
    11. }
    12. virtual void prepare() const //抽象工厂的重点是这个函数
    13. {
    14. std::cout << "准备 " + getName() << std::endl;
    15. std::cout << "准备面团..." << std::endl;
    16. std::cout << "添加酱汁..." << std::endl;
    17. std::cout << "添加配料: " << std::endl;
    18. for (auto& topping : this->toppings)
    19. {
    20. std::cout << " " + topping + "\n";
    21. }
    22. }
    23. virtual void bake() const
    24. {
    25. std::cout << "350 度 烤 25 分钟 "<< std::endl;
    26. }
    27. virtual void cut() const
    28. {
    29. std::cout << "切分披萨"<< std::endl;
    30. }
    31. virtual void box() const
    32. {
    33. std::cout << "披萨装盒" << std::endl;
    34. }
    35. virtual ~Pizza() = default;
    36. };

    主要的变化是我们将用到工厂内的组件也就是工厂家族,具体的实现过程抽象工厂实现。首先看一下批量获取相关原料的工厂接口。

    1. class PizzaIngredientFactory
    2. {
    3. public:
    4. virtual Dough CreatDough() = 0;
    5. virtual Cheese CreatCheese() = 0;
    6. .
    7. .
    8. .
    9. };

     我们可以根据不同的披萨商店,形成不同的具体原材料工厂实例

    1. class NYStylePepperoniPizza: public PizzaIngredientFactory
    2. {
    3. Dough createDough()
    4. {
    5. return new ThinCrustDough();
    6. }
    7. Cheese createCheese()
    8. {
    9. return new MarinaraCheese();
    10. }
    11. .
    12. .
    13. .
    14. }

    我们用一个例子来实现。我们让披萨制作阶段运行时调整,通过原材料工厂获取不同的原材料

    1. #include "Pizza.hpp"
    2. class NYStylePepperoniPizza: public Pizza
    3. {
    4. PizzaIngredientFactory _ingFact;
    5. public:
    6. NYStylePepperoniPizza(PizzaIngredientFactory ingFact) //通过工厂元素获取原料
    7. {
    8. this._ingFact = ingFact;
    9. }
    10. void prepare() //通过原料工厂批量获取对象
    11. {
    12. dough = _ingFact.createDough();
    13. sauce = _ingFact.createsauce();
    14. cheese = _ingFact.createcheese();
    15. }
    16. };

    最后我们需要更改披萨商店调用原材料工厂

    1. #include "Pizza.hpp"
    2. #include "PizzaStore.hpp"
    3. class NYPizzaStore: public PizzaStore
    4. {
    5. public:
    6. std::unique_ptr makePizza(std::string type)
    7. {
    8. std::unique_ptr pizza = nullptr;
    9. PizzaIngredientFactory idFact = new NYPizzaIngredientFactory (); //拿到原材料工厂
    10. if (type == "cheese")
    11. {
    12. pizza = NYStyleCheesePizza(idFact); //通过不同的工厂加工生成不同的披萨
    13. }
    14. else if (type == "pepperoni")
    15. {
    16. pizza = NYStylePepperoniPizza(idFact);
    17. }
    18. return pizza;
    19. }
    20. };

    这样就实现了不同工厂通过抽象工厂获得批量不同的元素,生成对应的披萨。

    总结:通过引进一个抽象工厂提供了为披萨家族创建家族。此工厂提供接口,我们通过这个接口获取对象。我们可以通过更换工厂获得不同的行为。

    抽象工厂提供一个接口来创建相关或依赖对象的家族,而不需要提供具体类

    七.对比工厂模式和抽象工厂

    抽象工厂通过对象组合创建对象,通过提供一个抽象接口,规定如何生产相关产品,工厂方法通过子类创建对象。

    抽象工厂用于提供大批量相关产品家族。

    工厂用于从具体的实例化解耦。

    抽象工厂往往会在具体实例化使用工厂模式

    抽象工厂缺点:添加新产品意味着改变接口。

    总结:

    抽象工厂提供一个接口,用于创建相关依赖对象的家族,而不必指定它们的具体类。依靠对象组合实现,子类创建在工厂接口暴露的方法中实现。抽象工厂的意图是创建相关对象家族,不必依赖具体类

    工厂方法,定义一个创建对象的接口,但让子类决定哪个类实例化,工厂方法让一个类延迟实例化到子类。工厂方法依赖继承,对象创建被委托给子类,子类实现工厂方法来创建对象。工厂方法的意图是允许类延迟实例化

  • 相关阅读:
    vue2后台管理系统
    zk分布式Job 实现的业务逻辑
    2021年互联网大厂的中秋仪式感
    从基础到卷积神经网络(第14天)
    LM-INFINITE: SIMPLE ON-THE-FLY LENGTH GENERALIZATION FOR LARGE LANGUAGE MODELS
    Elasticsearch 8.9启动时构建接收Rest请求的hander过程源码
    AOP获取通知以及实际应用
    等级保护测评—Linux(CentOs)身份鉴别
    spark常用的调参详解
    【WebLogic】Oracle发布2024年第二季度中间件安全公告
  • 原文地址:https://blog.csdn.net/m0_62572672/article/details/133932935