第二次个人作业---熟悉使用工具

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,自动化的回归测试更是可以大幅降低系统测试、维护升级等阶段的成本。
三、这次项目更重要的,我觉得是带给我了我信心,让自己知道有能力完成一个小项目。原来是很缺乏这种流程的,希望自己以后能通按照类似的方式完成更多的项目,进一步提高自己的代码能力和软件业务能力。

posted @ 2019-09-16 10:41  Dreamone11  阅读(203)  评论(4编辑  收藏  举报