大话设计模式解读01-简单工厂模式
本系列的文章,来介绍编程中的设计模式,介绍的内容主要为《大话设计模式》的读书笔记,并改用C++语言来实现(书中使用的是.NET中的C#),本篇来学习第一章,介绍的设计模式是——简单工厂模式。
1 面向对象编程
设计模式依赖与面向对象编程密不可分,因此在开始学习设计模式之前,先简单介绍下面向对象编程。
先来看一个小故事:
话说三国时期,曹操在赤壁带领百万大军,眼看就要灭掉东吴,统一天下,非常高性,于是大宴文武。
在酒席间,不觉吟到:“喝酒唱歌,人生真爽,…”,众文武齐呼:“丞相好诗!”,
于是一臣子速速命令印刷工匠进行刻版印刷,以便流传天下。

印刷工匠刻好样张,拿出来给曹操一看,曹操感觉不妥,
说道:“喝与唱,此话过俗,应该改为对酒当歌较好!”,
于是臣子就命令工匠重新来过,工匠眼看连夜刻版之功,彻底白费,心中叫苦不迭,只得照办。

印刷工匠再次刻好样张,拿出来给曹操过目,曹操细细一品,觉得还是不好,
说:“人生真爽太过直接,应该改为问句才够意境,因此应改为对酒当歌,人生几何”,
当臣子再次转告工匠之时,工匠晕倒…

那,问题出在哪里呢?
大概是三国时期还没有活字印刷术吧,所以要改字的时候,就必须整个刻板全部重新雕刻。
如果有了活字印刷术,其实只需要更改四个字即可,其余工作都未白做。

我们联想编程,从这个小故事中,来体会一下编程中的一些思想:
- 可维护:要改字,只需更改需要变动的字即可
- 可复用:这些字并不是只是这次有用,后续如果在其它印刷中需要用,可重复使用
- 可扩展:如果诗中需要加字,只需另外单独刻字即可
- 灵活性:字的排列可以横排,也可以竖排

面向对象编程,通过封装、继承和多态,把程序的耦合度降低。
传统印刷术的问题就在于把所有字都刻在同一个版面上的耦合度太高。
使用设计模式可以使程序更加灵活,容易修改,并易于复用。

2 计算器实例
下面以一个计算器的代码实例,来体会封装的思想,以及简单工厂模式的使用。
题目:设计一个计算器控制台程序,输入为两个数和运算符,输出结果
功能比较简单,先来看第一个版本的实现。
2.1 版本一:面向过程
第一个版本采用面向过程的思想,从接收用户输入,到数据运算,以及最后的输出,都是按顺序在一个代码块中实现的:
int main()
{float numA = 0;float numB = 0;float result = 0;char operate;bool bSuccess = true;printf("please input a num A:\n");scanf("%f", &numA);printf("please input a operate(+ - * \\):\n");std::cin >> operate;printf("please input a num B:\n");scanf("%f", &numB);switch(operate){case '+':{result = numA + numB;break;}case '-':{result = numA - numB;break;}case '*':{result = numA * numB;break;}case '/':{if (numB == 0){bSuccess = false;printf("divisor cannot be 0!\n");break;}result = numA / numB;break;}default:{bSuccess = false;break;}}if (bSuccess){printf("%f %c %f = %f\n", numA, operate, numB, result);}else{printf("[%f %c %f] calc fail!\n", numA, operate, numB);}return 0;
}
该程序的运行效果如下图所示:

上述代码实现本身没有什么问题,但是,如果现在要再实现一个带有UI界面的计算器,代码能不能复用呢?很显然不行,代码都是在一起的。
因此,为了便于代码复用,可以将计算部分的代码和显示部分的代码分开,降低它们之间的耦合度。
2.2 版本二:对业务封装
版本二则是对计算部分的业务代码和显示部分的控制台输入输出代码分开。
计算部分的业务代码,设计一个Operation运算类,通过其成员函数GetResult来实现加减乘除运算。
2.2.1 业务代码
class Operation
{
public:bool GetResult(float numA, float numB, char operate, float &result){bool bSuccess = true;switch(operate){case '+':{result = numA + numB;break;}case '-':{result = numA - numB;break;}case '*':{result = numA * numB;break;}case '/':{if (numB == 0){bSuccess = false;printf("divisor cannot be 0!\n");break;}result = numA / numB;break;}default:{bSuccess = false;break;}}return bSuccess;}
};
2.2.2 控制台界面代码
显示部分的控制台输入输出代码,还在main函数中。
int main()
{float numA = 0;float numB = 0;float result = 0;char operate;printf("please input a num A:\n");scanf("%f", &numA);printf("please input a operate(+ - * \\):\n");std::cin >> operate;printf("please input a num B:\n");scanf("%f", &numB);Operation Op1;bool bSuccess = Op1.GetResult(numA, numB, operate, result);if (bSuccess){printf("%f %c %f = %f\n", numA, operate, numB, result);}else{printf("[%f %c %f] calc fail!\n", numA, operate, numB);}return 0;
}
版本二的运行效果演示如下:

上述的版本二的代码实现,就用到了面向对象三大特性中的封装。
那,上述代码,是否可以做到灵活扩展?
比如,如果希望增加一个开根号的运算,如果改?
按照现有逻辑,需要修改Operation运算类,在switch中增加一个分支。但这样,会需要加减乘除的逻辑再次参与编译,另外,如果在修改开根号的代码时,不小心改动了加减乘除的逻辑,影响就大了。
因此,可以使用面向对象中继承和多态的思想,来实现各个运算类的分离。
2.3 版本三:简单工厂
版本三用到了封装、继承、多态,以及通过简单工厂来实例化出合适的对象。
2.3.1 Operation运算类(父类)
Operation运算类为一个抽象类,是加减乘除类的父类。
该类包含numA和numB两个成员变量,以及一个虚函数GetResult用于计算运算结果,各个子类中对其进行具体的实现。
// 操作类(父类)
class Operation
{
public:float numA = 0;float numB = 0;public:virtual float GetResult(){return 0;};
};
2.3.2 加减乘除类(子类)
加减乘除子类通过公有继承Operation类,可以访问其共有成员变量numA和numB,并对GetResult方法进行具体的实现:
// 加法类(子类)
class OperationAdd : public Operation
{
public:float GetResult(){return numA + numB;}
};// 减法类(子类)
class OperationSub : public Operation
{
public:float GetResult(){return numA - numB;}
};// 乘法类(子类)
class OperationMul : public Operation
{
public:float GetResult(){return numA * numB;}
};// 除法类(子类)
class OperationDiv : public Operation
{
public:float GetResult(){if (numB == 0){printf("divisor cannot be 0!\n");return 0;}return numA / numB;}
};
2.3.3 简单运算工厂类
为了能方便地实例化加减乘除类,考虑使用一个单独的类来做这个创造实例的过程,这个就是工厂。
设计一个OperationFactory类来实现,这样,只要输入运算的符号,就能实例化出合适的对象。
// 简单工厂模式
class OperationFactory
{
public:Operation *createOperation(char operation){Operation *oper = nullptr;switch(operation){case '+':{oper = (Operation *)(new OperationAdd());break;}case '-':{oper = (Operation *)(new OperationSub());break;}case '*':{oper = (Operation *)(new OperationMul());break;}case '/':{oper = (Operation *)(new OperationDiv());break;}default:{break;}}return oper;}
};
使用版本三,如果后续需要修改加法运算,只需要修改OperationAdd类中的内容即可,不会影响到其它计算类。
2.3.4 控制台界面代码
显示部分的控制台输入输出代码,还在main函数中。
通过多态,返回父类的方式,实现对应运算的计算结果。
{float numA = 0;float numB = 0;float result = 0;char operate;printf("please input a num A:\n");scanf("%f", &numA);printf("please input a operate(+ - * \\):\n");std::cin >> operate;printf("please input a num B:\n");scanf("%f", &numB);OperationFactory opFac;Operation *oper = nullptr;oper = opFac.createOperation(operate);if (oper != nullptr){oper->numA = numA;oper->numB = numB;result = oper->GetResult();printf("%f %c %f = %f\n", numA, operate, numB, result);delete oper;}else{printf("[%f %c %f] calc fail!\n", numA, operate, numB);}return 0;
}
版本三的运行效果演示如下:

版本三中,各个类之间的关系如下图所示:
- 运算类是一个抽象类(类名用斜体表示),具有两个float类型的公有的(共有用**+号**)成员变量numA和numB以及一个GetResult公有方法
- 四个计算类继承(继承用空心三角+实线表示)运算类,并实现对应的GetResult方法
- 简单工厂类依赖于(依赖用箭头+虚线表示)运算类,通过createOperation方法实现运算类的实例化

3 总结
本篇主要介绍设计模式中的简单工厂模式,首先通过一个活字印刷的小故事来体会程序设计中的可维护、可复用、可扩展、灵活性的思想,并引入面向对象设计模式中的三大基本思想:封装、继承、多态,然后通过一个计算器的代码实现的例子,通过C++实现了三个版本的代码,由浅到深地理解面向对象的设计思想以及简单工厂模式的使用。
相关文章:
大话设计模式解读01-简单工厂模式
本系列的文章,来介绍编程中的设计模式,介绍的内容主要为《大话设计模式》的读书笔记,并改用C语言来实现(书中使用的是.NET中的C#),本篇来学习第一章,介绍的设计模式是——简单工厂模式。 1 面向对象编程 …...
35python数据分析numpy基础之setdiff1d求两个数组的差集
1 python数据分析numpy基础之setdiff1d求两个数组的差集 python的numpy库的setdiff1d(x,y)函数,表示数组x与y的差,即在x且不在y中的元素,且进行去重排序。 用法 numpy.setdiff1d(ar1, ar2, assume_uniqueFalse)描述 numpy.setdiff1d(ar1,…...
JVM 指针压缩
运用java内存对齐填充,对java内存进行8字节划分,java对象指针映射到每个划分区域上,使得4个字节(32位)表示2^32个地址,从而使4个字节指针映射32G内存空间。 1.为什么进行指针压缩: jvm从32位变…...
时序预测 | Matlab灰色-马尔科夫预测
目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab灰色-马尔科夫预测 灰色马尔科夫预测(Grey-Markov Prediction)是一种用于时间序列预测的方法,它结合了灰色系统理论和马尔科夫链模型。灰色系统理论是一种非参数化的预测方法…...
代码界的奥斯卡:SpringBoot测试的艺术与科学
探索SpringBoot测试的神秘世界,揭秘如何成为代码质量的守护神!从基础环境搭建到高级集成测试,本系列教程带你一步步构建坚不可摧的测试防线。深入JUnit 5的强大功能,学习如何用MockMvc和Testcontainers打造逼真的测试场景。准备好…...
安防监控视频平台LntonCVS视频监控汇聚平台遏制校园暴力保护校园学生安全应用方案
未成年人被誉为祖国的花朵,是我们国家的未来。然而,最近频繁曝出的未成年霸凌事件却引发了社会的广泛关注。这些事件手段残忍,事态恶劣,引发了全社会对如何保护未成年身心健康、规避霸凌事件发生的深刻思考。 为了更好地保障学生的…...
Python | 平均绩点
字符串的概念和特点 字符串既可以使用单引号,也可以使用双引号""来创建 可以使用运算符来拼接字符串,并返回字符串拼接后的结果。 first_name "Tom" last_name "Jerry" full_name first_name " " &quo…...
2024年有什么值得入手的5G长期套餐大流量卡推荐?大流量手机卡入手指南(超4款正规手机卡实测总结)
前言 24年有什么值得入手的5G大流量卡推荐?大流量手机卡入手指南(超4款正规手机卡实测总结) 四大运营商有哪些大流量卡,可电话,非物联网卡 所有卡激活后,均可以在官方app可查、 所有都是优惠长期 5G大流…...
《尚上优选》项目Bug记录
写在前面 本项目为该系列第二个项目,有一些问题如果没有在本文摘录,可以到 《云尚办公》项目 BUG记录 中查找是否有类似的解决方案。 (2024.3.24以下) (P11) 管理端前端node20版本启动报OpenSSL错误 经典问题,把we…...
Flutter 中的 PopupMenuTheme 小部件:全面指南
Flutter 中的 PopupMenuTheme 小部件:全面指南 Flutter 是一个由 Google 开发的跨平台 UI 框架,它允许开发者使用 Dart 语言构建美观、响应式的移动、Web 和桌面应用。Flutter 的 Material 组件库中包含了丰富的 UI 组件,其中 PopupMenuButt…...
uni-app的网络请求库封装及使用(同时支持微信小程序)
其实uni-app中内置的uni.request()已经很强大了,简单且好用。为了让其更好用,同时支持拦截器,支持Promise 写法,特对其进行封装。同时支持H5和小程序环境,更好用啦。文中给出使用示例,可以看到使用变得如此…...
力扣524. 通过删除字母匹配到字典里最长单词
给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。 示…...
【代码随想录】【算法训练营】【第27天】 [39]组合总和 [40] 组合总和II [131]分割回文串
前言 思路及算法思维,指路 代码随想录。 题目来自 LeetCode。 day26, 休息的周末~ day 27,周一,库存没了,哭死~ 题目详情 [39] 组合总和 题目描述 39 组合总和 解题思路 前提:组合的子集问题&…...
解决 git 命令 Problem with the SSL CA cert (path? access rights?)
/etc/pki/nssdb 错误 运行命令: GIT_CURL_VERBOSE1 git clone git_repo_url 会输出详细错误信息 Cloning into fp_sdk... * Couldnt find host xxx.com in the .netrc file; using defaults * About to connect() to xxx.com port 443 (#0) * Trying 10.44.52.7…...
详解:重庆耶非凡的选品师项目有哪些优势?
在竞争激烈的电商市场中,重庆耶非凡科技有限公司凭借其独特的选品师项目,成功地在众多企业中脱颖而出。这一项目不仅体现了公司对市场趋势的敏锐洞察力,更彰显了其专业的选品能力和对消费者需求的深刻理解。 首先,耶非凡的选品师项…...
DSP28335模块配置模板系列——GPIO配置模板
在自己的电脑上构建出一套模块配置模板,可以大幅节省DSP程序开发时间,从而达到事半功倍的效果。对于初学者,掌握了模块配置,也就能实现大部分的单片机功能。 在DSP28335模块配置模板系列,不仅会给出GPIO、ADC、EQEP、E…...
【SringBoot项目中MyBatis-Plus多数据源应用实践】
文章目录 前言 一、Mybatis-Plus是什么? 二、多数据源是什么? 三、使用步骤 1. 新建一个SpringBoot项目 2. 引入必要的MyBatis架包 3. 新建两个数据库及两张表 3.3.1 新建数据库:DB_A,并创建一张数据表alarm_kind,以及…...
Android 图表开发开源库 MPAndroidChart 使用总结
1. 引言 电视项目中需要一个折线图表示节电数据变化情况,类比 H5 来说,Android 中也应该有比较成熟的控件,经过调研后,发现 MPAndroidChart 功能比较强大,网上也有人说可能是目前 Android 开发最好用的一个三方库了&a…...
手机号脱敏
手机号脱敏 // 手机号脱敏subTelephone(telphone) {let result telphone.substr(0, 4) **** telphone.substr(8);return result;},...
java基础篇(1)
JDK是什么?有哪些内容组成?JDK是Java开发工具包 JVM虚拟机: Java程序运行的地方 核心类库: Java已经写好的东西,我们可以直接用开发工具: javac、java、jdb、jhat.. JRE是什么?有哪些内容组成? JRE是Java运行环境 JVM、核心类库、运行工具 JDK,JRE&…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
