C#【结对编程作业】小学数学习题助手

 一、软件成品展示


 

  • 软件本体下载(包括程序及其更新日志,源码工程包,UML图,API接口文档,算法介绍文档,算式计算excel实例,浅查重程序)
  • 计算模块
  • 运算式及答案生成
    •   
  • 批改模块
  • 异常报告
      • Type:InvalidDataException

        运算符数量过大, 容易导致程序在运行计算模块生成答案时内存溢出,因此限定数量上限为10

         

        Type:InvalidDataException

        数值上界与运算符数过大,易导致算式产生超long整型范围的分数,导致计算出错,因此限定:

        Log(数值上界,2)*2*运算符数目 < 64

         

        Type:InvalidOperationException

        括号不匹配,容易引发越界访问问题,实质上对应非法栈操作,因此需检验算式并抛出该异常

         

        Type:FormatException

        算式格式不正确,无法解析

         

        Type:DividedByZeroException

        算式中不能出现除0操作

         

        Type:DividedByZeroException

        算式中分数分母不能为0

         

        Type:InvalidDataException

        算式中分数的分子或分母超过long整型范围

         

        Type:InvalidDataException

        在生成算式时会进行查重操作,如重复则重新生成。如果5分钟内程序无法生成新算式,则程序判定生成数量达到上界,仅输出生产部分并报出异常,这里只有6个式子:

        1+1;1-1;0+1(或1+0);1-0;0-0;0+0

         

        Type:FileNotFoundException

        执行批改程序时默认读取当前目录下的Exercise.txt和Answer.txt文件(当然用户也可以浏览选择自己的文件,默认编码为Unicode),如文件不存在则会报相应的异常

 

 

二、结对编程感想及总结


  •  合作照片展示:
  • 结对编程的感悟
    •   两个人一起合作编程是非常愉快的事情,当然,如果不用牺牲美好的国庆假期的话。
    •   江昊同学负责做前端,相应的我保证后端功能正确即可,设计整个计算核心大约消耗了整整三天左右,因为前一次个人项目中已经较好的封装了计算核心模块,所以这几天就是不断地加入新的参数控制,并排查错误,增添异常处理等等。期间通过江昊的测试反馈,找到了很多前一次作业中仍有疏漏的bug,可见结对编程还是很有效率的,更新好的后端立刻就可以拿到另一个人写好的前端里检验,有了未经处理的异常前端也能及时给出错误报告,很多参数范围不需要在后端代码里进行有效性检查,前端就可以通过输入栏进行限制,可以说是方便了不少。江昊写的前端我用起来也很舒服,如果发现了问题,直接用自己重新封装好的dll替换程序包里的dll就能用上新的。这样一边设计,一边测试,一边更新,很有真正开发一款软件的感觉。两个人协作编程,不仅提高了开发效率,也有助于更好地维护程序,使得我们能够更快速的发现问题并解决问题。不过结对编程同时也要求两个人的工作时间尽可能统一,并且需要大量的沟通来确保前后端的准确对接,不仅需要后端充分考虑接口调用的简易性,也要求前端写的界面尽可能兼容各种不同的后端类库,可见如果两人的合作时间仅占据各自独立开发一小部分,又不加强沟通的话,结对编程的效率会大大降低,如果把因为沟通问题而把大量时间花在理解对方的代码上,结对编程的作用就不再那么明显了。
    •   江昊同学的优点在于,针对我对前端提出的要求,能够很快的相应并作出修改,需要增加相应的测试单元也能很快实现,可以说执行力非常之高;此外,善于沟通,出现问题第一时间向我反馈,使得我可以及时作出更新修改,并且在他的帮助下我们得以和刘乾小组,杨墨犁小组实现前后端交换对接;而且做事也很有耐心,期间我们的程序从v0.9.0.0一路更新到v1.1.0.7,大大小小的修改更新也有二十余次了,能够一次又一次不厌其烦的为我后端的更新修改相应前端的说明和测试单元,这一点我很佩服,能够与这样的同学结对编程我十分荣幸。当然,如果在程序设计上更细致一些,江昊同学会是非常完美的结对编程伙伴的。
    •   我的缺点在于太精益求精了= =。。。自己给自己整一堆没用的功能然后又弄出一堆bug来,然后不停地更新后端让队友帮忙修改前端,说实话我自己都会觉得自己有点烦23333333 当然最后的效果还算不错,也算是解脱自己了吧,优点的话,时间多,熬得起,动力足。

 

三、 Information Hiding, interface design, loose coupling 的应用

 


 

  • Information Hiding
    •   信息隐藏,与面向对象的封装概念较为类似,我作为负责后端的程序员,主要做的就是封装的工作,尽可能避免重要信息外泄给调用者,从而引发不可预测的异常,我在自己编写的后端dll文件中,通过以下几种方式体现这一设计方法:
      • 1.  除了提供接口的core类外,其它类都用默认的internal修饰符修饰,使得除core类外,其它类都不可实例化,作为dll应用时不可见
      • 2.  core类的所有属性全部用private修饰,仅能通过接口函数select访问并修改
      • 3.  尽量避免通过返回值或ref修饰的返回参数来向外传值,在内部处理产生的所有信息,向外部提供的信息仅限于抛出异常,设计接口时大部分为void方法
  • Interface Design
    •   接口设计,接口是用于交互的,所以在设计接口时,主要需要考虑的是,“我们会接受哪些信息”和“我们需要反馈哪些信息”,有些接口,比如生成表达式,既不需要接受信息也不需要反馈(当然抛出异常除外),那就设计成不接受任何参数的void函数,我理解的接口设计围绕两个字展开,那就是“抠门”,既不过多向前端请求一些无用的信息,也不向前端反馈一些不便于处理的内容,将信息处理尽可能集成到dll内部,所有的参数都设置的恰到好处。当然设计接口最后在了解用户需求的情况下与前端设计者达成协议,这样也有便于自己的设计。C#可以为传入的参数设置默认值,这样传入参数的时候如果少传了也可以正常使用,当然我们也可以通过重载函数来实现。灵活的接口设计,既要做到信息精简,也要做到广泛兼容。我在最上面的程序包里提供自己的API接口说明,试着写这样一份文档也有助于我更好的帮助自己反思接口设计需要注意的一些地方。
  • loose coupling
    •   松耦合,松耦合要求最小化依赖,实现可伸缩性、灵活性和容错。这一点不仅仅是对于后端,对于前段也有很高的要求。对于后端而言。需要在API接口设计上做到普适,提供各种重载的方法来应对不同的应用场景,并在内部处理好异常信息,使得使用者调用时或者不出错,或者一旦出错,后端里也为其写好了处理函数,得到的是经过处理的异常信息,有明确的错误定位。对于前端而言,需要做好输入规范,需要考虑从UI获得的各种信息并设计相应的数据结构,以便传递给不同的后端程序,对于后端抛出的异常需要采用统一的处理方案,并尽可能避免自己发生异常,从而保证传入后端的参数是始终有效的。只有做到这一点,后端才能对接更多的前端,前端才能应用于更多的后端。不过松耦合设计的代价会有很多冗余性的工作,如果只是开发私人项目,不需要做太多维护工作的话,松耦合并非十分必要。
    •   在我们的结对编程项目中,实现了松耦合,我们的后端与杨墨犁小组的前端,我们的前端与刘乾小组的后端分别实现了完美的对接,下面是截图:
      •   与杨墨犁小组的前端对接:
      •   
      •   与乾麻小组的后端对接:

 

四、Design By Contract

 


  • 契约式设计,就是把类和他的客户程序之间的关系看做正式的协议,描述双方的权利和义务,被Bertrand Meyer称作构建面向对象软件系统方法的核心。

    契约式设计的提出主要基于软件可靠性的考虑,包括正确性与健壮性。正确性指软件按照需求规格执行的能力,健壮性指软件对需求规格中未声明状况的处理能力。健壮性主要与异常处理机制相关。契约即客户按照需求规格使用软件,软件设计者按照需求规格设计软件,需求规格之外的请求造成的软件使用错误责任不由软件设计者承担。契约式设计是一套机制,在客户称需要提供者之间明确地声明双方的责任与权力,即契约,并能够对这些契约进行验证。

    我们在“小学数学习题助手”软件开发过程中,对于绝大部分正常请求给予支持,并进行了正确性验证。对于极端情况的数据,比如生成题目数量非常大而生成数值上限与运算符数量非常小的情况,我们进行了响应的异常报错处理机制,并设计良好的用户交互,通知异常原因。我们的软件开发过程其实是在遵循契约式设计的原则。

    契约式设计的原则的优点在于,确保了server与client地位的平等,双方有各自的义务与责任,这样就保证了代码的质量,提高了软件工程的效率与质量。

    契约式设计也有缺点,那就是契约式设计需要一种机制来验证契约的成立与否,断言就是最好的选择,但并不是所有的程序语言都有断言机制,那么强行使用语言进行模仿就势必造成代码的冗余和不可读性的提高,比如.NET4.0以前就没有assert的概念。

 

五、 Unit Test


  • 通过单元测试,我们完成了对程序基本功能(计算,表达式生成,批改,读入参数)的检验,并成功捕捉到错误输入时相应的各类异常(见前面展示部分),展示图如下所示:
  • PS:UML图和算法核心思想在顶部程序包里,欢迎自取ww

 

posted @ 2015-10-07 00:02  kibbon  阅读(774)  评论(2编辑  收藏  举报