大话设计模式解读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++实现了三个版本的代码,由浅到深地理解面向对象的设计思想以及简单工厂模式的使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mixly 开启WIFI AP UDP收发数据

一、开发环境 软件:Mixly 2.0在线版 硬件:ESP32-C3(立创实战派) 固件:ESP32C3 Generic(UART) 测试工工具:NetAssist V5.0.1 二、实现功能 ESP32开启WIFI AP,打印接入点IP地址,允许…

模拟堆-java

模拟堆也是对堆的一次深入理解和一些其它操作,可以了解一下。 文章目录 前言 一、模拟堆 二、算法思路 1.结点上移 2.结点下移 3.插入一个数 4.输出当前集合的最小值 5.删除当前集合的最小值(数据保证此时的最小值唯一) 6.删除第k个插入的数 …

Springboot健身房管理系统-计算机毕业设计源码44394

摘 要 大数据时代下,数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求,利用互联网服务于其他行业,促进生产,已经是成为一种势不可挡的趋势。在健身房管理的要求下,开发一款整体式结构的健身房管理系统…

代理 模式

一、什么是代理模式 代理模式指代理控制对其他对象的访问,也就是代理对象控制对原对象的引⽤。在某些情况下,⼀个对象不适合或者不能直接被引⽤访问,⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 二、为什么使用代理模式 模式作…

HW面试常见知识点2——研判分析(蓝队中级版)

🍀文章简介:又到了一年一度的HW时刻,本文写给新手想快速进阶HW蓝中的网安爱好者们, 通读熟练掌握本文面试定个蓝中还是没问题的!大家也要灵活随机应变,不要太刻板的回答) 🍁个人主页…

揭秘下载数据背后的秘密,Xinstall助你掌握市场脉搏

在当下这个移动互联网时代,应用推广已成为各大企业竞争的重要战场。然而,如何有效地获取并分析应用下载数据,却成为了许多推广者面临的难题。今天,我们将为大家介绍一款强大的应用推广助手——Xinstall,它能够帮助你轻…

隐藏 IP 地址的重要性是什么?

在当今的数字时代,保护我们的在线身份至关重要。从保护个人信息到保护隐私,互联网用户越来越多地寻求增强在线安全性的方法。保持匿名和保护敏感数据的一个关键方面是隐藏您的 IP 地址。在这篇博文中,我们将深入探讨隐藏 IP 地址的重要性&…

人脸识别技术与人证合一智能闸机的剖析

人脸识别技术,作为一种先进的生物认证手段,依据个体面部独有的特征信息来进行身份验证。这项技术通过捕获图像或视频中的面部数据,执行一系列精密步骤,包括图像获取、面部定位、预处理、特征提取与比对,以确认个人身份…

【JMeter接口自动化】第2讲 Jmeter目录结构

JMeter的目录结构如下: bin目录:可执行文件目录,启动jmeter时,就是启动bin目录下的ApacheJmeter.jar,jmeter.bat,jmeter.sh ApacheJmeter.jar:启动文件 jmeter.bat:Windows 的启动命令。 jmeter…

前端框架前置知识之Node.js:fs模块、path模块、http模块、端口号介绍

什么是模块? 类似插件,封装了方法 / 属性 fs 模块- 读写文件 代码示例 // 1. 加载 fs 模块对象 const fs require(fs) // 2. 写入文件内容 fs.writeFile(./test.txt, hello, Node.js, (err) > {if (err) console.log(err) //若 err不为空&#xf…

韩顺平0基础学java——第15天

p303-326 重写override 和重载做个对比 注:但子类可以扩大范围,比如父类是protected,子类可以是public 多态 方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。 多态的具体体现…

绕过WAF(Web应用程序防火墙)--介绍、主要功能、部署模式、分类及注入绕过方式等

网站WAF是一款集网站内容安全防护、网站资源保护及网站流量保护功能为一体的服务器工具。功能涵盖了网马/木马扫描、防SQL注入、防盗链、防CC攻击、网站流量实时监控、网站CPU监控、下载线程保护、IP黑白名单管理、网页防篡改功能等模块。能够为用户提供实时的网站安全防护&…

Java开发:Spring Boot 实战教程

序言 随着技术的快速发展和数字化转型的深入推进,软件开发领域迎来了前所未有的变革。在众多开发框架中,Spring Boot凭借其“约定大于配置”的核心理念和快速开发的能力,迅速崭露头角,成为当今企业级应用开发的首选框架之一。 《…

git拉去代码报错“Failed to connect to 127.0.0.1 port 31181: Connection refused“

最近参与了一个新项目,在使用git clone 克隆代码时遇到了一个报错"fatal: unable to access ‘https://example.git/’: Failed to connect to 127.0.0.1 port 31181: Connection refused",今天就和大家分享下解决过程。 报错详情 在使用git clone 克隆…

【JavaEE】Servlet

文章目录 一、Servlet 是什么二、如何创建Servlet程序1、创建项目2、引入依赖3、创建目录4、编写代码5、打包程序6、部署程序7、验证程序 一、Servlet 是什么 二、如何创建Servlet程序 1、创建项目 2、引入依赖 Maven 项目创建完后,会自动生成一个 pom.xml 的文…

coze自定义插件调用3

1,打开我的空间; 2,编辑,选择快捷指令 3,编辑指令 4,实际测试【输入框多了一个按钮“查询基础信息”,点击查询基础信息,提示输入缴费卡号,提交后如下图】

HTTP --tcp和keep-alive

TCP TCP连接 tcp/ip是全球计算机以及网络设备都在使用的一种常见的分组交换网络分层协议集,客户端可以打开一条tcp/ip连接,连接到可能运行在世界各地的服务器应用程序,一旦连接建立起来了,在客户端和服务器的计算机之间交换的报…

ar地产沙盘互动体验提供更加丰富多彩的楼盘信息

AR增强现实技术作为其重要分支,正逐步在全球市场中崭露头角。国内的AR增强现实技术公司正致力于链接物理世界和虚拟世界,为用户带来沉浸式的AR体验。它们打造线上线下联动的一站式文旅景区数字化运营平台,让您在享受旅游的同时,也…

用容器构建wordpress项目

用容器构建wordpress项目 #准备两个镜像 #数据库和centos docker pull mysql:5.7 docker pull centos:7 #创建一个wordpress文件夹,在wordpress文件里面写一个Dockerfile文件 vim DockerfileFROM centos:7 #基于centos环境RUN yum -y install epel-release ;\ #安装…

vue3状态管理,pinia的使用

​​​​​​​状态管理 我们知道组件与组件之间可以传递信息,那么我们就可以将一个信息作为组件的独立状态(例如,单个组件的颜色)或者共有状态(例如,多个组件是否显示)在组件之传递&#xff0c…