第二次个人作业---熟悉使用工具
GIT地址 | https://github.com/DreamOne11 |
GIT用户名 | DreamOne11 |
学号后五位 | 62207 |
博客地址 | https://www.cnblogs.com/dreamone11/ |
作业链接 | <作业要求> |
1、环境配置
-
这次我使用的VS2017,下面是我的环境配置。在此之前就已经安装好了。
(注意:校园网络可能导致VS的更新速度缓慢,所以更新慎点。解决方式:使用其他网络下载更新,或是把网络属性取消勾选IPV6协议) -
Git的安装
首先我们下载Git工具并安装。因为我之前自已一直使用的时Github Desktop,所以再次项目的克隆和上传都是使用的Github Desktop。两者在功能上并没有本质性的区别,只是后者设计出了图形界面比较方便操作。
项目需求
阿超家里的孩子上小学一年级了,这个暑假老师给家长们布置了一个作业:家长每天要给孩子出一些合理的,但要有些难度的四则运算题目,并且家长要对孩子的作业打分记录。
作为程序员的阿超心想,既然每天都需要出题,那何不做一个可以自动生成小学四则运算题目与解决题目的命令行 “软件”呢。他把老师的话翻译一下,就形成了这个软件的需求:程序接收一个命令行参数 n,然后随机产生 n 道加减乘除(分别使用符号+-*/来表示)练习题,每个数字在 0 和 100 之间,运算符在 2 个 到 3 个之间。
由于阿超的孩子才上一年级,并不知道分数。所以软件所出的练习题在运算过程中不得出现非整数,比如不能出现 3÷5+2=2.6 这样的算式,运算过程中不允许出现负数。
练习题生成好后,将生成的 n 道练习题及其对应的正确答案输出到一个文件 subject.txt 中。
注:项目需求来自博客:https://www.cnblogs.com/ChildishChange/p/10398212.html
2、代码设计
代码思路
-
Equation类:
用于生成等式的等式类,想要随机实现一个等式要注意几个要点:
1、随机生成操作数(0-100)
2、随机生成操作符(+,-,*,/)
3、操作符的数量要在两个或三个之中随机选择
4、操作数个数=操作符个数+1(所以我们可以采用循环一次一次叠加生成最终的等式)
按照上述要点,我选择先进行第一个操作数(numOne)的随机然后确定操作符数量后进入循环。在循环中进行后续操作数(numX)的随机和操作符号的随机 -
Compute类:
用于计算Equation生成等式的计算类,我们生成的等式被保存在string中,并不是真正的计算式。我们可以使用DataTable的compute方法来对数据表中的string进行计算。 -
WriteToFile类:
用于把我们计算完成的式子存入文件,这里要注意写入文件方法的使用。 -
主函数入口类:
用于满足用户输入生成的题目数量,并且调用其他类的方法
代码实现
-
Equation类:
public static string creatEquation() { char[] symbol = { '+', '-', '*', '/' }; string formula=null; Random ran = new Random(); int numOne=ran.Next(0,101); int numX; int index; int symbolNum = ran.Next(2, 4); List<int> result = new List<int>(); formula = formula+numOne; //循环生成计算式,因为有几个运算符就要循环几次 for (int i=0;i<symbolNum;i++) { numX = ran.Next(0, 101); index = ran.Next(0, 4); if(!result.Contains(index))//生成不重复随机数 { result.Add(index); } else { symbolNum++; continue; } if (numX == 0) //防止除数为0 { continue; } formula = formula + symbol[index] + numX; } return formula; }
-
Compute类:
public class Compute { public static string compute(string formula) { DataTable dt = new DataTable(); object result=dt.Compute(formula, null); while(result.ToString().Contains('.')|| result.ToString().Contains('-')) { formula = Equation.creatEquation(); result = dt.Compute(formula, null); } return formula+'='+result; } }
-
WriteToFile类:
class WriteToFile { public static void save(string finallResult) { try { if(finallResult!=null) { using (StreamWriter sw = new StreamWriter("c:/Users/15199/Desktop/subject.txt", true)) { sw.WriteLine(finallResult); } } } catch { Console.WriteLine("写入文档失败!"); } } }
关键问题及解决方法
从代码实现开始到结束,大大小小共有六个问题困扰过我。解决这六个问题的过程也就是我完成项目的过程,所以以下问题按时间出现顺序排序:
-
问题一:类之间的调用问题
解决方法:因为有一段时间没有接触过C#,对于面向对象语言有一点生疏。为了体现面向对象语言特性并且方便单元测试,所以还是针对不同的功能设计了不同的类。对于类之间的和类中方法的调用其实直接点用就可以了,要注意的是:普通方法:需要实例化对象,用对象点出来;静态方法:直接用类名点出来,不需要实例化对象。 -
问题二:如何避免一个等式中频繁出现除号(生成不重复随机数)
解决方法:为了让生成的等式中不频繁的出现' / '号,那么就要设计生成不重复随机数。我采用的是用List列表保存上一个随机出来的操作符,在下一个操作符随即出来后,进行是否已经包含在List中的判断操作,如果已经包含则说明是已经生成过的操作符,那么就重新生成操作符。具体代码在上面代码实现部分。 -
问题三和问题四:如何用string变量保存等式、如何计算string 变量中所保存的等式
解决方法:因为两个问题关联性很大,所以就一起给出。在起初我思考如何计算生成的等式时,我想的是利用编译器计算能力直接进行分步计算,但是这对于题目中的种种条件限制会将问题复杂化,于是我决定一次性计算生成的式子。方法就锁定在DataTable的Compute方法,这个方法可以直接对表达式进行计算,前提是我们要把表达式存放在string中。但是计算生成的结果是Object类的变量,所以我们还需要进行强制转换为string就可以把答案也保存在string变量中。 -
问题五:等式中出现一个运算符或没有运算符
解决方法:在运行测试阶段,反馈结果会出现只有一个操作符的情况。明明已经做了在2-4个操作个数中进行随机,可是为什么又出现了只有一个操作符呢?
上面是我完成项目之后重新修改了代码的截图,在这之前是没有注释掉的这句话的,于是会导致逻辑上的错误。当第二个操作符和第一个操作符重复之后,它虽然不会被添加进计算式但是还是跳出了循环,当只循环两次时就不会再添加操作符了,所以我在发生这种情况时让操作数个数加一,便可以继续循环。 -
问题六:文件写入时,只写入了最后一个等式。
解决方法:在方法选用上我一开始选择的方法会重新覆盖文档,所以造成了问题,如图:
于是我去网上查了一下,了解到写入的追加写入方法,更新了自己的代码,如下图:
3、单元测试、回归测试、效能测试
-
配置单元测试
我是第一次接触单元测试,所以按照指导建立了单元测试项目,如下:
然后我们可以针对所需要的测试创建单元测试(注意:所需测试的类和方法必须是公开的 public很关键!),如下:
-
我们对计算等式模块和生成等式模块进行分开的单元测试,我认为单元测试最关键的步骤是设计测试代码,因为单元测试最好可以覆盖所有代码路线。在关键点的测试上也是最重要的,所以我针对项目需求中的一些限制条件进行了测试代码的设计,如下:
- 生成等式模块测试:
- 计算等式模块测试:
- 生成等式模块测试:
-
回归测试
回归测试的重要性在于,当你的项目再进行修改之后很有可能不会使项目更好,反而会使项目出现新的问题。比如,当小学生的计算水平加强时,计算当中可以加入分数(小数)的计算,我们此时会修改代码,如下:
此时我们会发现,原来的测试报错了,我们就需要针对新的代码去编写新的测试代码! -
断点的调试
断点的使用频率很高,并且很多问题都可以通过断点设置后的调试找到,在本次编程的过程中,上述的问题五的发现和解决就是我通过设置断点找到的,如下:
在设置完断点后,对程序进行单步调试,此时可以观察变量的变化,当然也可以查看内存中具体数据的变化! -
效能测试
在这里我们使用VS2017的性能探测器来看一下CPU的使用率,为了代码可以运行足够长时间来保证性能测试的准确性,我们先生成10000个计算式。但我在效能分析的过程中我就发现了程序的一个弊端,由于为了使random随机出不同的式子,我设置了睡眠,但是睡眠时间有些长,让整个性能测试的时间都托了很久。于是我修改了睡眠时间,加快了等式的生成速度。如下图:
我们可以看到整个项目中的不同的类在总CPU耗时占比
也可以进入一个方法中查看具体代码的占比,如下:
4、项目克隆、提交与Github Desktop的使用
-
步骤一:将四则运算拷贝到自己的项目中
-
步骤二:使用Github Desktop克隆项目到本地
-
步骤三:使用VS在本地的的克隆项目中新建文件,并命名为Github的id
-
前三步为开始实现代码打好了基础,然后使用Github Desktop进行每次修改代码后的上传
-
上传完成后我们登陆网页Github,对自己的项目进行查看。最终再pull request到班级仓库中
5、总结与反思
一、这次作业的完成,让我对Git工具有了更深的理解和更熟练的运用,说一下我对Git和Github Desktop使用的选择和理解吧。它们的目的是一样的,只不过后者有图形页面,前者是命令行形式。在本次作业中我使用了后者,不过为了更加了解Git的使用,我也下载了Git工具并且对其进行了文件下载和上传的练习。新手刚接触时,GIt的命令容易输错,需要仔细敲命令行。相比较而言Github Desktop图形界面操作简单,不过因为其图形化界面有限,很多Git可以实现的功能无法在图形页面上找到。两者的选择要根据我们自己项目的实际情况。
二、单元测试让我更加感到了项目的严谨性,无论项目是大是小,一个完整的流程会让我们的项目更具有价值。单元测试也会提升程序的复用性和正确性,回归测试让我们的程序在修改之后保证快速寻找bug,自动化的回归测试更是可以大幅降低系统测试、维护升级等阶段的成本。
三、这次项目更重要的,我觉得是带给我了我信心,让自己知道有能力完成一个小项目。原来是很缺乏这种流程的,希望自己以后能通按照类似的方式完成更多的项目,进一步提高自己的代码能力和软件业务能力。