C++设计模式 - 创建型模式之工厂模式

文章目录

  • C++设计模式 - 创建型模式之工厂模式
      • 接口和针对接口编程
    • 1. 简单工厂模式
      • 适用场合
      • UML
      • 代码示例
    • 2. 工厂方法模式
      • 适用场合
      • UML
      • 代码示例
    • 3. 抽象工厂模式
      • 适用场合
      • UML
      • 代码示例
    • 总结

C++设计模式 - 创建型模式之工厂模式

工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式。听上去差不多,都是工厂模式。

工厂模式的主要作用是封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展

接口和针对接口编程

接口从语法层面来说,是纯虚类。从软件设计的意义来说,通常用接口来定义类的外观,约定了实现类应该要实现的功能。封装变化——即隔离变化。从软件的整体结构来看,只要接口不变,内部实现不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性。

1. 简单工厂模式

它的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。有点抽象,举个例子就明白了。有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显示地告诉生产工厂。

适用场合

在程序中,需要创建的对象很多,导致对象的构建复杂时,需要使用简单工厂模式;
由于对象的创建过程是我们不需要去关心的,而我们注重的是对象的实际操作,所以,我们需要分离对象的创建和操作两部分,如此,方便后期的程序扩展和维护。

UML

在这里插入图片描述

代码示例

#include <bits/stdc++.h>

using std::cout;
using std::endl;

//
//简单工厂模式
//


typedef enum PhoneTypeTag {
    MI_PHONE,
    IPHONE_PHONE,
    HUAWEI_PHONE
} PhoneType;


//产品抽象基类 AbstractProduct
class Phone {
    //添加虚析构函数!
public:
    virtual void type() = 0;//代表共有属性
};

//
//具体的产品类
//

class MiPhone : public Phone {
public:
    void type() override {
        cout << "I'm XiaoMi Phone !" << endl;
    }
};

class IPhone : public Phone {
public:
    void type() override {
        cout << "I'm IPhone !" << endl;
    }
};

class HuaweiPhone : public Phone {
public:
    void type() override {
        cout << "I'm Huawei Phone !" << endl;
    }
};

//工厂类 手机代工厂
class PhoneFactory {
public:
    //根据产品信息创建具体的产品类示例,返回一个抽象产品类
    std::shared_ptr<Phone> MakePhone(const PhoneType type) {
        switch (type) {
            case PhoneType::MI_PHONE:
                return std::make_shared<MiPhone>();
            case PhoneType::IPHONE_PHONE:
                return std::make_shared<IPhone>();
            case PhoneType::HUAWEI_PHONE:
                return std::make_shared<HuaweiPhone>();
            default:
                return nullptr;
        }
    }

};

int main() {
    PhoneFactory phoneFactory;
    std::shared_ptr<Phone> m_phone = nullptr;

    m_phone = phoneFactory.MakePhone(PhoneType::MI_PHONE);
    m_phone->type();

    m_phone = phoneFactory.MakePhone(PhoneType::IPHONE_PHONE);
    m_phone->type();

    m_phone = phoneFactory.MakePhone(PhoneType::HUAWEI_PHONE);
    m_phone->type();

    //动态绑定
    //m_phone = std::make_shared<MiPhone>();
    //m_phone->type();

    return 0;
}

代码运行输出结果如下:

I'm XiaoMi Phone !
I'm IPhone !
I'm Huawei Phone !
  • 主要特点:产品创建过程在工厂类中完成,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
  • 缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。

2. 工厂方法模式

  • 其实这才是正宗的工厂模式,简单工厂模式只是一个简单的对创建过程封装。

  • 和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂

在代码实现上,工厂方法模式在简单工厂模式的基础上增加对工厂的基类抽象,不同的产品创建采用不同的工厂创建(从工厂的抽象基类派生),这样创建不同的产品过程就由不同的工厂分工解决:FactoryA专心负责生产ProductA,FactoryB专心负责生产ProductB,FactoryA和FactoryB之间没有关系;如果到了后期,如果需要生产ProductC时,我们则可以创建一个FactoryC工厂类,该类专心负责生产ProductC类产品。

适用场合

工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅提供具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

UML

在这里插入图片描述

代码示例

#include <bits/stdc++.h>

using std::cout;
using std::endl;

//
//工厂方法模式
//

//产品抽象基类 AbstractProduct
class Phone {
    //添加虚析构函数!
public:
    virtual void type() = 0;//代表共有属性
};

//
//具体的产品类
//

class MiPhone : public Phone {
public:
    void type() override {
        cout << "I'm XiaoMi Phone !" << endl;
    }
};

class IPhone : public Phone {
public:
    void type() override {
        cout << "I'm IPhone !" << endl;
    }
};

class HuaweiPhone : public Phone {
public:
    void type() override {
        cout << "I'm Huawei Phone !" << endl;
    }
};

//抽象工厂类,提供一个创建接口
class PhoneFactory {
public:
    //提供创建产品实例的接口,返回抽象产品类
    virtual std::shared_ptr<Phone> MakePhone() = 0;
};

//具体的创建工厂类,使用抽象工厂类提供的接口,各具体的工厂去创建具体的产品实例
class MiPhoneFactory : public PhoneFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<MiPhone>();
    }
};

class IPhoneFactory : public PhoneFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<IPhone>();
    }
};

class HuaWeiPhoneFactory : public PhoneFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<HuaweiPhone>();
    }
};


int main() {
    std::shared_ptr<Phone> m_pPhone = nullptr;

    m_pPhone = MiPhoneFactory().MakePhone();
    m_pPhone->type();

    m_pPhone = IPhoneFactory().MakePhone();
    m_pPhone->type();

    m_pPhone = HuaWeiPhoneFactory().MakePhone();
    m_pPhone->type();

    return 0;
}
  • 该模式相对于简单工厂模式的优势在于:便于后期产品种类的扩展。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。
  • 缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
工厂方法减轻了工厂类的负担,新增一种“手机”只需添加一个特定的“手机工厂”即可,这就符合了开放闭合原则:

3. 抽象工厂模式

  • 工厂方法自然比简单工厂更加灵活,但当业务有所变更时,比如需要添加“手机”的周边产品——“电脑”呢?这时候我们就需要一个更复杂的模式——抽象工厂

  • 抽象工厂是一个产品簇的概念,一个工厂可以生产多种业务相关的产品。我们在工厂方法的基础上扩充一下代码:定义一个抽象工厂接口AbstractFactory,通过不同的方法生产出一个“抽象”产品簇(Phone和PC)。回过头来再看工厂方法,事实上它就是抽象工厂最简单的一种场景设计——只生成一种产品。

  • 抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。

适用场合

工厂方法模式适用于产品种类结构单一的场合,为一类产品提供接口;
而抽象工厂方法适用于产品种类结构多的场合,主要用于创建一组(有多个种类)相关的产品,为它们提供创建的接口。

UML

在这里插入图片描述

代码示例

#include <bits/stdc++.h>

using std::cout;
using std::endl;

//
//抽象工厂模式
//

//手机产品抽象基类 AbstractProduct
class Phone {
    //添加虚析构函数!
public:
    virtual void type() = 0;//代表共有属性
};

//
//具体的手机产品类
//

class MiPhone : public Phone {
public:
    void type() override {
        cout << "I'm XiaoMi Phone !" << endl;
    }
};

class IPhone : public Phone {
public:
    void type() override {
        cout << "I'm IPhone !" << endl;
    }
};

class HuaweiPhone : public Phone {
public:
    void type() override {
        cout << "I'm Huawei Phone !" << endl;
    }
};


//PC产品抽象基类 AbstractProduct
class PC {
    //添加虚析构函数!
public:
    virtual void type() = 0;//代表共有属性
};

//
//具体的手机产品类
//

class MiPC : public PC {
public:
    void type() override {
        cout << "I'm XiaoMi PC !" << endl;
    }
};

class MACPC : public PC {
public:
    void type() override {
        cout << "I'm MAC PC !" << endl;
    }
};

class HuaweiPC : public PC {
public:
    void type() override {
        cout << "I'm Huawei PC !" << endl;
    }
};



//抽象工厂类,提供一个创建接口,其中增加PC产品接口
class AbstractFactory {
public:
    //提供创建产品实例的接口,返回抽象产品类
    virtual std::shared_ptr<Phone> MakePhone() = 0;
    virtual std::shared_ptr<PC> MakePC() = 0;
};



//具体的创建工厂类,使用抽象工厂类提供的接口,各具体的工厂去创建具体的产品实例
class MiFactory : public AbstractFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<MiPhone>();
    }

    std::shared_ptr<PC> MakePC() override{
        return std::make_shared<MiPC>();
    }
};

class IPhoneFactory : public AbstractFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<IPhone>();
    }
    std::shared_ptr<PC> MakePC() override{
        return std::make_shared<MACPC>();
    }
};

class HuaWeiFactory : public AbstractFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<HuaweiPhone>();
    }
    std::shared_ptr<PC> MakePC() override{
        return std::make_shared<HuaweiPC>();
    }
};

//另外一个代工厂,生产MiPhone和MAC
class OdmFactory : public AbstractFactory {
public:
    std::shared_ptr<Phone> MakePhone() override {
        return std::make_shared<MiPhone>();
    }
    std::shared_ptr<PC> MakePC() override{
        return std::make_shared<MACPC>();
    }
};



int main()
{
    std::shared_ptr<Phone> m_pPhone = nullptr;
    std::shared_ptr<PC> m_pPC = nullptr;

    MiFactory miFactory;
    IPhoneFactory iPhoneFactory;
    HuaWeiFactory huaWeiFactory;
    OdmFactory odmFactory;

    m_pPhone = miFactory.MakePhone();
    m_pPC = miFactory.MakePC();
    m_pPhone->type();
    m_pPC->type();

    m_pPhone = iPhoneFactory.MakePhone();
    m_pPC = iPhoneFactory.MakePC();
    m_pPhone->type();
    m_pPC->type();

    m_pPhone = huaWeiFactory.MakePhone();
    m_pPC = huaWeiFactory.MakePC();
    m_pPhone->type();
    m_pPC->type();

    m_pPhone = odmFactory.MakePhone();
    m_pPC = odmFactory.MakePC();
    m_pPhone->type();
    m_pPC->type();


    return 0;
}

代码输出如下:

I'm XiaoMi Phone !
I'm XiaoMi PC !
I'm IPhone !
I'm MAC PC !
I'm Huawei Phone !
I'm Huawei PC !
I'm XiaoMi Phone !
I'm MAC PC !

在抽象工厂模式中,增加新的产品族很方便(如可以将IPhone和HuaWeiPC作为一个产品系列,只用增加一个工厂即可),但是增加新的产品等级结构很麻烦(如,增加键盘作为新的产品,那么不仅需要增加新的产品类,同时各个工厂也需要进行对应的调整,将键盘这一产品增加进去)。

总结

工厂模式最直观的理解是,减少new创建对象的方式,用接口的方式来返回一个对象,而new创建的方式被封装了

  • 简单工厂:调用者只需使用单例工厂就可获取同一范畴的所有产品

  • 工厂方法:调用者并不知道它在运行时会获取何种产品,只知道某个特定的工厂能生成出满足需求的产品

  • 抽象工厂:调用者可以在运行时从特定的工厂中获得所有信息相关的产品簇(可以对产品进行组合)

工厂方法模式和抽象工厂模式区别:区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构,抽象工厂模式针对的是面向多个产品等级结构的。

是否可以这样理解呢:工厂方法模式生产一种产品,抽象工厂模式生产一系列产品,构成的是一个生态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27852.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣 209. 长度最小的子数组

一、题目描述 给定一个含有 n 个正整数的数组和一个正整数 target。 找出该数组中满足其和大于等于 target 的长度最小的连续子数组&#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0。 示例 1&#xff1a; 输入&#xff1a;target 7, nums [2,3,1…

Uni-app学习从0到1开发一个app——(3)简单小工程内容介绍

文章目录 工程文件 看看一个标准的hello微信小程序工程文件的组成和作用。 工程文件 可以参考官方教程&#xff1a;传送门 之前的文章有详细的开发环境介绍&#xff0c;传送门Uni-app学习从0到1开发一个app——(2)windowns环境搭配&#xff0c;这里我们先建一个简单的示例微信…

C++教程(06)——变量类型

C 变量类型 变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有指定的类型&#xff0c;类型决定了变量存储的大小和布局&#xff0c;该范围内的值都可以存储在内存中&#xff0c;运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母…

华为OD机试真题B卷 Java 实现【自守数】,附详细解题思路

一、题目描述 自守数是指一个数的平方的尾数等于该数自身的自然数。例如&#xff1a;252 625&#xff0c;762 5776&#xff0c;93762 87909376。 请求出n(包括n)以内的自守数的个数。 数据范围&#xff1a; 1≤n≤10000 二、输入描述 int型整数。 三、输出描述 n以内…

大话Stable-Diffusion-Webui-动手开发一个简单的stable-diffusion-webui(一)

文章目录 写在前面整体效果开发所需环境开发工具需要具备的知识Node安装更改npm包安装的目录设置npm镜像vscode安装创建vue项目代码编写项目先体验注意写在前面 stable-diffusion-webui(以下简称sd)项目通过FastAPI对外提供了一系列的api用于开发者二次开发或者集成到自己的…

005Mybatis返回值(ResultMap 一对多,多对多)

属性 id 应该总是指定一个或多个可以唯一标识结果的属性。 虽然&#xff0c;即使不指定这个属性&#xff0c;MyBatis 仍然可以工作&#xff0c;但是会产生严重的性能问题。 只需要指定可以唯一标识结果的最少属性。显然&#xff0c;你可以选择主键&#xff08;复合主键也可以…

MixQuery系列(一):多数据源混合查询引擎调研

背景 存储情况 当前的存储引擎可谓百花齐放,层出不穷。为什么会这样了?因为不存在One for all的存储,不同的存储总有不同的存储的优劣和适用场景。因此,在实际的业务场景中,不同特点的数据会存储到不同的存储引擎里。 业务挑战 然而异构的存储和数据源,却给分析查询带…

Vue中如何进行状态持久化(LocalStorage、SessionStorage)

Vue中如何进行状态持久化&#xff08;LocalStorage、SessionStorage&#xff09;&#xff1f; 在Vue应用中&#xff0c;通常需要将一些状态进行持久化&#xff0c;以便在用户关闭浏览器或刷新页面后&#xff0c;仍能保留之前的状态。常见的持久化方式包括LocalStorage和Sessio…

【软件环境安装部署】华为云服务器下 Docker 安装 MongoDB 以及 SpringBoot 整合 MongoDB 开发使用

文章目录 安装测试 MongoDB拉取镜像创建和启动容器登录mongo容器&#xff0c;并进入到【admin】数据库创建一个用户&#xff0c;mongo 默认没有用户连接mongo数据库测试数据库&#xff0c;插入一条语句测试数据库&#xff0c;查询刚才插入的语句查看所有数据库开放指定端口开放…

华为存储IA篇仿真器搭建

设备清单 编号 设备名 数量 备注 01 Windows系统主机 1台 为VMware提供安装位置 02 VMware软件 1份 提供存储仿真器的部署环境 03 仿真器文件 1份 用于部署estor虚拟机 【注意】&#xff1a;暂无注意事项 一、下载安装文件并配置虚拟机设备清单 1.1…

动态测试数据处理

分类 动态测试数据&#xff1a; 1、确定性数据&#xff1a;能够用明确的数学表达式进行描述的数据称为确定性数据。 Ⅰ、周期数据 Ⅱ、非周期数据 2、随机性数据&#xff1a;无法用明确的数学表达式表述&#xff1b;若在一个…

Qt详解实现TCP文件传输例子(文件下载和上传)附源码

网络通信我们用的很频繁&#xff0c;如文字&#xff0c;语音&#xff0c;文件&#xff0c;图片等&#xff0c;这个些传输方式都差不多 QT文件传输主要考验对传输的控制&#xff0c;还是需要点逻辑的&#xff0c;文件传输的大致框架如下 先看一下简单例子实现的效果&#xff08…

【伏羲八卦图】(PythonMatlab实现)

目录 1 与达尔文对话 2 与老子对话 2.1 Python实现 2.2 Matlab实现 1 与达尔文对话 140年前&#xff0c;1858年7月1日&#xff0c;达尔文在英伦岛发表了自己有关自然选择的杰出论文。他提出&#xff0c;生物的发展规律是物竞天择。经过物竞&#xff0c;自然界选择并存留最具…

k8s实践之mysql集群搭建(十五)

先下载 k8s实践之mysql集群搭建资料 主从模式简介&#xff1a; 当master主服务器上的数据发生改变时&#xff0c;则将其改变写入二进制&#xff08;binlog&#xff09;事件日志文件中&#xff1b; slave从服务器会在一定时间间隔内对master主服务器上的二进制日志进行探测&am…

【算法题解】38. 括号的生成

这是一道 中等难度 的题 https://leetcode.cn/problems/generate-parentheses/ 题目 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数&#xff0c;用于能够生成所有可能的并且 有效的 括号组合。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;["…

废柴日记8:从入门到入狱的Python爬虫学习笔记1(入门篇)

前言&#xff1a;我错了&#xff0c;但下次也不一定(●’◡’●) 米娜桑&#xff0c;好久不见&#xff0c;不知道这段时间各位手中的西瓜刀有没有按时擦亮呢&#xff1f; 我也是在摸爬滚打将近一年之后总算是找到了一点人生的方向所以当成救命稻草现在正死死握紧不放手的啊。…

java八股文-并发篇

并发篇 1. 线程状态 要求 掌握 Java 线程六种状态掌握 Java 线程状态转换能理解五种状态与六种状态两种说法的区别 六种状态及转换 分别是 新建 当一个线程对象被创建&#xff0c;但还未调用 start 方法时处于新建状态此时未与操作系统底层线程关联 可运行 调用了 start …

2023全国计算机二级考试时间(全年各阶段考试时间安排)

2023全国计算机二级考试时间(全年各阶段考试时间安排) 2023年全国计算机二级考试时间分别为&#xff1a;3月25日至27日(上半年3月)、9月23日至25日(下半年9月)。 其中3月和9月开考全部级别全部科目&#xff0c;5月和12月考试开考一、二级全部科目&#xff0c;各省级承办机构可根…

SQL锁总结

一、概述 介绍 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源(CPU、RAM、I/O)的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&#xff0c;锁…

12 VI——变分推断

文章目录 12 VI——变分推断12.1 背景介绍12.2 Classical VI12.2.1 公式导出12.2.2 坐标上升法 12.3 SGVI——随机梯度变分推断12.3.1 一般化MC方法12.3.2 降方差——Variance Reduction 12 VI——变分推断 12.1 背景介绍 变分推断的作用就是在概率图模型中进行参数估计&…