【HeadFirst 设计模式】工厂模式的C++实现

ops/2024/10/18 10:18:50/

文章目录

  • 简单工厂模式
    • 一、案例背景
    • 二、案例分析
    • 三、代码分析
  • 工厂方法模式
    • 一、案例背景
    • 二、案例分析
    • 三、代码分析
  • 抽象工厂模式
    • 一、案例背景
    • 二、案例分析
    • 三、代码分析

简单工厂模式

一、案例背景

你是小镇上最有名的一家披萨店的店主,你们店里的披萨广受人们喜爱。你们的订单系统是这么写的:

Pizza* orderPizza(string type)
{Pizza* pizza {};if (type == "cheese"){pizza = new CheesePizza();}else if (type == "greek"){pizza = new greekPizza();}else if (type == "pepperoni"){pizza = new PepperoniPizza();}pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;
}

二、案例分析

看起来很正常。但是压力来自于增加更多的披萨类型。你发现你所有的竞争者都已经在他们的菜单中加入了一些流行风味的披萨,很明显,你必须要赶上他们,所以你也要将这些类型的披萨加进你的菜单中,而最近Greek Pizza卖的不好,所以你决定将它从菜单中去掉。

#include <iostream>
#include <string>
using namespace std;Pizza* orderPizza(string type)
{Pizza* pizza {};if (type == "cheese"){pizza = new CheesePizza();}//else if (type == "greek")//{//    pizza = new greekPizza();//}else if (type == "pepperoni"){pizza = new PepperoniPizza();}else if (type == "clam"){pizza = new ClamPizza();}else if (type == "veggie"){pizza = new VeggiePizza();}pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;
}

前面提到过一个“开闭原则”,很明显地,如果实例化“某些”具体类,将使orderPizza()出问题,而且也无法让orderPizza()对修改关闭,但是,现在我们已经知道哪些会改变,哪些不会改变,是时候使用封装了。

现在最好将创建对象移到orderPizza()之外,但是怎么做呢?要把创建披萨的代码移到另一个对象中,由这个新对象专职创建披萨。

三、代码分析

看了代码实现之后,你可能会想:简单工厂模式看起来“简单”了,简单的不像个设计模式。这样做有什么好处?似乎只是把问题搬到另一个对象里罢了,问题依然存在。别忘了,SimplePizzaFactory可以有很多客户,虽然目前只看到了订单系统对齐进行了调用,但是,可能还有其他类利用这个工厂获取披萨的价钱和描述。

class SimplePizzaFactory
{
public:Pizza* createPizza(const String& type){Pizza* pizza {};if (type == "cheese"){pizza = new CheesePizza();}else if (type == "greek"){pizza = new greekPizza();}else if (type == "pepperoni"){pizza = new PepperoniPizza();}else if (type == "clam"){pizza = new ClamPizza();}else if (type == "veggie"){pizza = new VeggiePizza();}return pizza;}
};
Pizza* orderPizza(string type)
{Pizza* pizza = SimplePizzaFactory().createPizza(type);pizza->prepare();pizza->bake();pizza->cut();pizza->box();return pizza;
}

工厂方法模式

一、案例背景

你的披萨店经营有成,击败了竞争者,现在大家都希望加盟你的店。身为加盟公司的经营者,你希望确保加盟店营运的质量,所以希望这些店都使用你那些经得起时间考验的代码。

在这个过程中,你发现加盟店的确是采用你的工厂创建披萨,但是其他部分,却开始采用他们自己根据当地人的口味做的一些改良:烘焙手法有些差异等等。再想想这个问题,你真的希望能够建立一个框架,把加盟店和创建披萨捆绑在一起的同时又保持一定的弹性。

二、案例分析

如果你已经学习过之前的设计模式,想必一定已经对面向接口编程有了一定的理解。既然我们想要根据不同的披萨店定制个性化的披萨,那就简单干脆一些,写出一个工厂父类,然后实现好几个具体工厂继承它,这样在创建的时候我们便能够动态的选择不同的工厂去制作披萨。

这样做确实可以,但是从不同的工厂获取原材料就意味着你对披萨的质量失去了一定的控制。这里提出另外一种做法:将工厂不再抽象为一个接口,而是抽象为一个方法,由这个方法获得具体的披萨实例。代码轮廓大概是这样的:

class PizzaStore
{
public:pizza orderPizza(const String& type){Pizza pizza = createPizza(type);pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;}virtual Pizza createPizza(const string& type) const = 0;
};

然后由具体的子类继承并实现自己需要的createPizza接口,这样一来,顾客决定到哪一家披萨店下订单才决定了披萨的风味。

三、代码分析

这里给出工厂方法模式的相关案例:

#include <iostream>
#include <string>
using namespace std;class Pizza
{   // 这里省略具体细节
public:void prepare() {}void bake() {}void cut() {}void box() {}
};class NYStylePizza : public Pizza
{
};
class NYStyleVeggiePizza : public Pizza
{
};
class NYStylePepperoniPizza : public Pizza
{
};class ChicagoStylePizza : public Pizza
{
};
class ChicagoStyleVeggiePizza : public Pizza
{
};
class ChicagoStylePepperoniPizza : public Pizza
{
};class PizzaStore
{
public:Pizza* orderPizza(const string& type){Pizza* pizza = createPizza(type);pizza->prepare();pizza->bake();pizza->cut();pizza->box();return pizza;}virtual Pizza* createPizza(const string& type) const = 0;
};class NYPizzaStore : public PizzaStore
{Pizza* createPizza(const string& type) const override{if (type == "cheese"){return new NYStylePizza();}else if (type == "veggie"){return new NYStyleVeggiePizza();}else if (type == "clam"){return new NYStylePepperoniPizza();}else{return nullptr;}}
};class ChicagoPizzaStore : public PizzaStore
{Pizza* createPizza(const string& type) const override{if (type == "cheese"){return new ChicagoStylePizza();}else if (type == "veggie"){return new ChicagoStyleVeggiePizza();}else if (type == "clam"){return new ChicagoStylePepperoniPizza();}else{return nullptr;}}
};int main()
{// 定制一个纽约风味的披萨Pizza* NYStyle      = (new NYPizzaStore())->orderPizza("cheese");// 定制一个芝加哥风味的披萨Pizza* ChicagoStyle = (new ChicagoPizzaStore())->orderPizza("cheese");return 0;
}

抽象工厂模式

一、案例背景

现在,披萨店的加盟生意如火如荼,但是随着时间的流逝,一些加盟店开始使用低价的原材料来增加利润。你必须采取一些手段,以免长此以往砸了自己的招牌。

如何确保所有加盟店的原材料使用高质量的原材料呢?你打算建造一家生产原料的工厂,并将原材料送到各家加盟店。

二、案例分析

现在,我们要建造一个工厂来生产原料,这个工厂将负责创建原料家族的每一种原料。也就是说,工厂还需要生产面团,酱料等。

先为工厂定义一个接口,这个接口负责创建所有品类的原材料:

class PizzaIngredientFactory
{
public:virtual Dough*   createDough()   = 0;virtual Sauce*   createSauce()   = 0;virtual Cheese*  createCheese()  = 0;virtual Veggies* createVeggies() = 0;virtual Clams*   createClams()   = 0;
};

然后创建具体的工厂实现:

class NYPizzaIngredientFactory : public PizzaIngredientFactory
{Dough* createDough() override{return new ThinCrustDough();}Sauce* createSauce() override{return new MarinaraSauce();}Cheese* createCheese() override{return new ReggianoCheese();}Veggies* createVeggies() override{return new NYVeggies();}Clams* createClams() override{return new FreshClams();}
};

之后就是将这些工厂与原来的代码进行合并。

三、代码分析

这里给出抽象工厂模式的相关案例:

#include <iostream>
#include <string>
using namespace std;class Dough
{
};
class ThinCrustDough : public Dough
{
};
class Sauce
{
};
class MarinaraSauce : public Sauce
{
};
class Cheese
{
};class ReggianoCheese : public Cheese
{
};
class Veggies
{
};
class NYVeggies : public Veggies
{
};
class Clams
{
};
class FreshClams : public Clams
{
};class PizzaIngredientFactory
{
public:virtual Dough*   createDough()   = 0;virtual Sauce*   createSauce()   = 0;virtual Cheese*  createCheese()  = 0;virtual Veggies* createVeggies() = 0;virtual Clams*   createClams()   = 0;
};class NYPizzaIngredientFactory : public PizzaIngredientFactory
{
public:Dough* createDough() override{return new ThinCrustDough();}Sauce* createSauce() override{return new MarinaraSauce();}Cheese* createCheese() override{return new ReggianoCheese();}Veggies* createVeggies() override{return new NYVeggies();}Clams* createClams() override{return new FreshClams();}
};class Pizza
{
protected:string   name    = nullptr;Dough*   dough   = nullptr;Sauce*   sauce   = nullptr;Cheese*  cheese  = nullptr;Veggies* veggies = nullptr;Clams*   clams   = nullptr;public:virtual void prepare() = 0;void         bake() {}void         cut() {}void         box() {}string getName(){return name;}void setName(string name){name = name;}
};class CheesePizza : public Pizza
{
private:PizzaIngredientFactory* factory;public:CheesePizza(PizzaIngredientFactory* factory) : factory(factory) {}void prepare() override   // 神奇的事情在这里发生{name    = "Cheese Pizza";dough   = factory->createDough();sauce   = factory->createSauce();cheese  = factory->createCheese();veggies = factory->createVeggies();clams   = factory->createClams();}
};class ClamPizza : public Pizza
{
private:PizzaIngredientFactory* factory;public:ClamPizza(PizzaIngredientFactory* factory) : factory(factory) {}void prepare() override   // ClamPizza的原料在这里拿到{name    = "Clam Pizza";cheese  = factory->createCheese();veggies = factory->createVeggies();clams   = factory->createClams();}
};class PizzaStore
{
public:Pizza* orderPizza(const string& type){Pizza* pizza = createPizza(type);pizza->prepare();pizza->bake();pizza->cut();pizza->box();return pizza;}virtual Pizza* createPizza(const string& type) const = 0;
};class NYPizzaStore : public PizzaStore
{
public:Pizza* createPizza(const string& type) const override{Pizza*                  pizza           = nullptr;PizzaIngredientFactory* instanceFactory = new NYPizzaIngredientFactory();if (type == "cheese"){pizza = new CheesePizza(instanceFactory);pizza->setName("New York Style Cheese Pizz");}else if (type == "clam"){pizza = new ClamPizza(instanceFactory);pizza->setName("New York Style Clam Pizz");}return pizza;}
};int main()
{// 定制一个纽约风味的披萨Pizza* NYStyle = (new NYPizzaStore())->orderPizza("cheese");return 0;
}

http://www.ppmy.cn/ops/94279.html

相关文章

58 mysql 存储引擎之 MEMORY

前言 我们这里来看一下 MEMORY 存储引擎, 我们常见的那些 临时表什么的, 都是基于 MEMORY 在之前 我们也曾经调试过 相关内存临时表的信息 它主要是 使用 hp_scan, hp_find_record 等等 api 来操作内存中的信息 我们这里基于 information_schema.TABLES 这张基于 MEMORY 的…

Rvt/dgn格式的模型如何提取外轮廓,用于压平倾斜模型或者地形,进行BIM+GIS融合

0序 很多设计院、施工单位都需要做BIMGIS的融合&#xff0c;把设计成果或者施工方案和现状实景做叠加。 BIM作为设计模型和现状的实景是不吻合的&#xff0c;多数都需要在现状的基础上进行改造&#xff0c;穿过村落的桥梁&#xff0c;已有立交的跨域等。为了更好的展示设计方案…

AQS源码解析(2)-共享锁的获取与释放

大家好&#xff0c;我是呼噜噜&#xff0c;好久没聊关于Java并发&#xff0c;本文接着上一篇文章图解ReentrantLock的基石AQS源码-独占锁的获取与释放&#xff0c;将继续聊聊AQS共享锁获取与释放的一些细节 共享锁与独占锁的区别 首先我们先了解知道共享锁与独占锁的区别&…

解决antd TreeSelect 返回值不包含父节点问题 -自定义组件(react)

在写一个表单时使用了antd的 TreeSelect&#xff0c;在对TreeSelect的值提交时发现&#xff0c;父节点的值在半选状态时未提交&#xff0c;在选中状态时&#xff08;子节点全选&#xff09;&#xff0c;子节点不提交&#xff0c;只提交父节点&#xff0c;这与后端需求不符&…

python发送外部请求

在Python中&#xff0c;服务器发送外部请求是一个常见的操作&#xff0c;尤其是在需要集成不同服务或API时。有多种库可以帮助你完成这项任务&#xff0c;但最流行和广泛使用的库之一是requests。以下是如何使用requests库在Python服务器中发送外部请求的基本步骤&#xff1a; …

408专业135|王道和二轮强化课的经验分享

408 进入第二轮复习阶段&#xff0c;主要任务是大量练习大题。 此时&#xff0c;不建议完整地观看强化课程&#xff0c;因为在第一轮复习中&#xff0c;你已经做了大量选择题&#xff0c;积累了丰富的经验&#xff0c;并且熟悉了题目的出题方式。然而&#xff0c;这并不意味着…

WebSocket 实现:注解与原生方式对比

WebSocket 作为一种在单个长连接上进行全双工、双向通信的协议&#xff0c;已经成为现代Web应用中实现实时通信的重要技术。本文将探讨如何使用注解和原生方式来实现 WebSocket&#xff0c;并对这两种方法进行比较。 一、注解方式实现 WebSocket 在许多现代Java框架中&#x…

《编程学习笔记之道:构建知识宝库的秘诀》

在编程的浩瀚世界里&#xff0c;我们如同勇敢的探险家&#xff0c;不断追寻着知识的宝藏。而高效的笔记记录和整理方法&#xff0c;就像是我们手中的指南针&#xff0c;指引着我们在这片知识海洋中前行&#xff0c;不至于迷失方向。在这篇文章中&#xff0c;我们将深入探讨如何…