个人博客 结对项目
Jaein&&Lee结对项目
合作编程剪影
我=。=在实现功能模块的封装,而jaein 在进行UI的设计(^.^)
软工这周的项目又告一段落啦~ 在此对我的Partner ---> jaein表示深深的感谢!感谢她在这一周的时间里,督促我,理解我,同时也能包容我。
结对编程收获
结对编程收获颇多,概括起来有如下三点:
- 之前的个人项目的进度都由自己来控制,有一定的随意性,而在结对编程中两个人要相互配合,互相帮助,互相督促,所以项目进度的控制则更加的规范合理。
- 之前的个人项目总是认为能实现基本功能,能按时完成就好,在结对项目中发现,你的马虎,你的懈怠是对你的队友的不负责,你只有选择更好!
- 在结对编程中,一味地固执己见,独断专行会让你们的项目根本进行不下去,更多的是需要两个人的沟通与交流,而且在一方陈述想法时,另一方要善于倾听,这样才能达到更好的效果。
结对编程不足
结对编程的缺点主要是如下的一个方面:
在代码风格不同这一问题各执己见,发生矛盾,进而影响工作情绪。
队友优点
- jaein非常耐心,在编程过程中,我的注意力有时候会不能集中,而她总是耐心地提醒我,让我继续投入到编程中。
- jaein的想法总是非常新颖,对于软件的界面设计有自己独特的思考,这一点我自愧不如,所以项目的UI全部由jaein来实现,而最后的效果也出奇的好。
- jaein对于软件的质量要求严格,对我如此,对自己的部分更是如此,为了设计更好地界面,她花费了大量时间来搜集资料,思考以及尝试,这种追求完美的心态我也自愧不如。
- jaein的国语很棒,跟她合作编程感觉沟通完全没有障碍,而且我也很佩服她能把多种语言说的非常流畅,(Ps.自愧不如++; (=_=''))
队友不足
她有时候会陷入莫名其妙的纠结当中,而在我看来这些纠结是完全没有必要的... (=.=)
设计方法
Information Hiding, interface design, loose coupling
Information Hiding这一设计思想体现在如下几个方面:
- 一个重要的方面体现在类的封装以及接口的设计。即对用户仅仅暴露出对外的接口,而具体的实现细节对于用户来说是不可见的,即在多层设计的层与层之间加入接口层,以及在所有类与类之间都通过接口类来访问。
- 另一个重要的方面体现在类的所有数据成员都是private,所有的访问都需要通过访问函数来实现。
Interface design主要体现在计算模块与界面模块的交互上面
考虑到效率的缘故,我们的软件核心的计算功能是用C实现的,而界面是用C#实现的,将核心的计算功能封装成DLL,并且在界面模块中调用相应的计算功能。针对这个项目设计了如下的6个接口:
- public extern static IntPtr generateExpression();
- public extern static IntPtr calculateExp(string inputstr);
- public extern static void setting(int OperatorNum, int Range, int ExcNum, int MuldivOrNot, int BracketsOrNot, int NegOrNot, int FractOrNot);
- public extern static int judgeNegInCalculate(string expression);
- public extern static int judgeDivideByZeroInCalculate(string expression);
- public extern static void problemForOutFile(string ExcOutFile, string AnsOutFile);
interface design主要为接口设计
各个接口含义如下:
- 随机生成一个四则运算表达式,返回表达式指针,并且通过Marshal.PtrToStringAnsi(generateExpression());转换为字符串。
- 给定一个合法的四则运算表达式计算值,并且返回结果指针,通过Marshal.PtrToStringAnsi( calculateExp(expression));转换为字符串。
- 指定所要生成四则运算表达式的各项参数,OperatorNum指定运算符个数上限,Range指定表达式中“数”的上限,ExcNum指定生成题目的数量(用于批量出题并导出),MuldivOrNot指定生成的四则运算表达式中是否允许有乘除法,BracketsOrNot指定生成的四则运算表达式中是否有括号,NegOrNot用于指定运算过程中是否允许出现负数,FractOrNot用于指定运算过程中是否出现分数。
- 给定一个合法的四则运算表达式判断计算过程中是否存在负数的情况,如果存在返回1,否则返回0;
- 给定一个合法的四则运算表达式判断计算过程中是否存在除零错误,如果存在返回1,否则返回0;
- 给定导出题目位置以及相应答案位置,批量出题并且输出至文件。
loose coupling设计思想主要体现在界面模块,测试模块和核心模块的松耦合。
结对编程方法
- Design by Contract
- Code Contract
含义:
- Design by Contract,契约式编程,是一种计算机软件的设计方法,这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,为传统的抽象数据类型增加了前置条件、后置条件和不变式,“契约”在此是一种比喻,因为它和商业契约的情况有点类似。
- 因为Design by Contract是属于Eiffel Software的注册商标,很多开发人员用契约式编程(Programming by Contract),契约编程(Contract Programming),或者契约优先式开发(Contract-First development)来指代这种方法。微软也采用这种设计方法,称为代码合约(Code Contracts)。
摘自维基百科。
优点:
契约式编程在一定程度上减轻了编码负担和测试负担,因为当使用契约编程时,程序员不需要对契约条件是否满足进行校验,而是由代码的调用者来保证前置条件的自动满足,并且只有在前置条件得到满足的情况下程序的执行才是有意义的,而不满足前置条件代码的执行情况未知,这种未知不需要由程序员来定义。
不足:
当程序的调用者在调用的过程中由于偶然的失误导致程序的前置条件没能满足,这时程序很有可能出现未定义的状况,比如死循环,或者崩溃的错误。
应用:
在这个项目中,需要实现契约编程的模块是:表达式的计算模块。
- 前置条件要求:传入的参数是合法的四则运算表达式字符串,四则运算符和等号前后需要空格隔开。
- 后置条件要求: 输出一个字符串,字符串的值为四则运算表达式的计算结果。
- 不变式约束 :在调用前后原表达式的字符串内容不发生任何变化。
为了保证前置条件得到满足,在表达式计算模块调用之前使用正则表达式对于输入的合法性进行验证。
Unit test
第一次测试发现代码的覆盖率为78.35%,分析结果显示generateExpression()的代码覆盖率较差,由于TestGenerateExp()测试中只测试了生成一个表达式,可能造成代码分支覆盖不完全,故而设置循环,随机生成100000个表达式,测试代码如下:
1 [TestMethod] 2 void TestGenerateExp() 3 { 4 for (int i = 0; i < 100000; i++) 5 { 6 generateExpression(); 7 } 8 };
改进的代码覆盖率如下图:
第二次测试发现代码的覆盖率为79.93%,我的心是崩溃的。。怎么会这样。。代码覆盖率居然几乎没有提高?!难道是我打开VS的姿势不对?!仔细想一想我使用setting()来设置随机生成表达式的参数,在未设置的情况下为默认的,而在默认情况下四则运算表达式的长度是3,长度有限,所以遍历的可能性有局限,setting()函数的参数以及含义写在前面API中,第一个参数为operatorNum,默认为3,在这里设置为25。
1 [TestMethod] 2 void TestGenerateExp() 3 { 4 5 for (int i = 0; i < 100000; i++) 6 { 7 setting(25, 120, 150, 1, 1, 0, 1); 8 generateExpression(); 9 } 10 11 };
改进的代码覆盖率如下:
我的心得到了些许的慰藉,果然代码覆盖率提高了不少。然而还不是特别理想,进一步考虑,generateExpression()中有许多参数的分支判断,而使用setting(25, 120, 150, 1, 1, 0, 1)来设置参数限制了分支的执行种类,故而测试代码改进为如下:
1 [TestMethod] 2 void TestGenerateExp() 3 { 4 srand((unsigned)time(NULL)); 5 for (int i = 0; i < 100000; i++) 6 { 7 setting(rand() % 20 + 1, rand() % 20 + 1, rand() % 20 + 1, rand() % 2, rand() % 2, rand() % 2, rand() % 2); 8 generateExpression(); 9 } 10 11 };
改进的代码覆盖率如下:
代码覆盖率提高到了91.15%,较为满意,然而generateExpression()还是存在有未覆盖到的代码,还可以继续优化.......
UML图
由于用C实现的基本功能,不存在类的概念,对于dll仅生成的相应的代码函数关系图如下:
界面的UML序列图如下:
界面的代码函数关系图如下:
算法核心
针对计算功能,首先根据正则表达式来过滤不合法的输入情况,然后通过接口调用C实现的计算模块。计算模块的实现主要是将两个数统一转换为分子/分母的形式,再进行运算。