网站建设网站维护网站外包,近两年成功的网络营销案例,php免费空间申请,电商网站首页图片切换怎么做的本系列的文章#xff0c;来介绍编程中的设计模式#xff0c;介绍的内容主要为《大话设计模式》的读书笔记#xff0c;并改用C语言来实现#xff08;书中使用的是.NET中的C##xff09;,本篇来学习第一章#xff0c;介绍的设计模式是——简单工厂模式。
1 面向对象编程
…本系列的文章来介绍编程中的设计模式介绍的内容主要为《大话设计模式》的读书笔记并改用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实现了三个版本的代码由浅到深地理解面向对象的设计思想以及简单工厂模式的使用。