结对编程人员:徐丞(13061203)赖彦谕(13061212)
照片:
各自优缺点:
徐丞:
优点:编程能力还可以,对C++掌握比较好;细致认真,查到好多bug;逻辑严密
缺点:面向过程的编程思想比较严重,函数体一般比较长,而且有大量的if-else逻辑判断
赖彦谕:
优点:服从安排合作 愿意提出想法 敢于接受任务
缺点:拖延症晚期 手癌晚期 代码能力并不是很强大
1.结对编程的优点&缺点:
优点: (1)两人合作一起编程能提高更好的设计质量和代码质量。遇到问题可以两个人一起进行讨论和思考,解决问题的能力得到了增强。
(2)提高了代码复审的效率和质量。结对编程中另一个人一直在进行代码复审,一段代码会被检查多次,而且由于两个人是一起编程,对代码的理解程度都是一样深入的,这为代码复审提高了质量。
(3)有助于集中个人注意力,并且尽可能编写出更好的代码。因为在结对编程中,你的所作所为都被另一个人所看到,这更像是一种在别人面前的表演,为了不使自己在同伴面前丢脸,自己一定会使尽全力。
(4)编程过程中两人是相互学习,相互帮助的,有助于提高两人的个人水平。
缺点:重点是需要前期的磨合,如果两人相处的不够融洽,关系紧张,相互敷衍,这不仅会使效率变得低下,也不会产生高质量的代码。
2.说明怎样利用好这些设计:
Information hiding:信息隐藏原则保证所封装的数据的安全性,即外部不能直接对内部的数据进行修改。而且要提供供外部访问的接口,外部可以直接调用该接口而不需要知道内部的细节。这一设计原则在面向对象的设计中尤为重要,类与类之间相互调用时,要保证外部不能直接修改内部的变量(所有的数据成员应该是private),从而保证了类变量的安全性。而且可以设计接口类实现类与类之间的访问,这样保证了类各自属性的安全性且有利于扩展且方便调用。
interface design:接口的设计是为了统一某种标准,比如说在编写程序时,有一组类,他们都有类似的行为,我们可以对这种行为定义一个接口,这些类去实现这个接口就可以了,当我们在实际使用时就不必再考虑具体的实现,从而减少了编程复杂度。
Loose coupling:松耦合的原则要求供应者和用户之间任意一方发生了问题都不能影响到另一方。在编写代码时,要注意降低模块(类)之间的依赖性,当一个模块(类)作出修改时,与之相关的模块(类)不必做过多修改且功能能够正常运行。通过提供API来供外部调用,类与类之间建立接口类来实现相互的调用,从而降低类与类之间的依赖性。
在我们的程序中,设计了3个类(操作数类,公式类,计算类),由计算类提供三个接口(calcs,generate,check)供外部调用,这样可以将内部的实现细节隐藏起来,外部调用时也不需要清楚内部的具体实现情况,只需要调用对应的API接口即可。并且外部想修改内部的类属性时,不能够直接对其赋值,需要调用setting接口实现间接的赋值。
3.Design by Contract&&Code Contract
Design by Contract:契约式编程由前置条件,后置条件,不变式组成,它明确的规定了在调用某个操作前后应当属于何种状态,所谓契约就是在代码实现之前,将契约建立好,并且保证实现的代码一定是满足三个条件的。如果满足契约则认为程序满足正确性,若否则程序是不正确的。
优点:(1)提高了可靠性,调用某种操作所处的前后状态以及不变式都是已知的,你可以清楚的知道你的程序再调用该方法后会发生哪些变化,处于何种状态。
(2)降低了代码间的依赖性,当A调用B时,此时若B的代码实现发生了一些改变,但只要B的契约没有改变,A依然可以调用B,并且保证程序依旧是正确的。
(3)提高了代码的可复用性,在考虑是否可以使用某一函数时,只需考察其契约是否满足需求,而不需要考虑其内部的具体实现。
(4)提高了代码间的可替换性,对于不同的函数实现,只要保证其契约含义相同,则在调用时可以相互替换。
(5)便于理解程序的结构和功能。
缺点:如果有一方没有遵守规约而没有被发现,再调用该方法时会带来很严重的问题,而且想要排除这一问题是需要很大工作量的。
我们这次编程总共设计了三个类Formula,Operand,StuCalculator,所有的API接口在StuCalculator类中提供,三个接口分别为calc,Generate,check。这样的设计方法,使外部再调用是不用关心内部的实现就可以实现其功能,更好的封装了内部
的变量和实现细节,对于接口的定义采用契约是编程的思想,对输入参数格式提出了若干要求,同时给出了接口调用完后所实现的操作。
4.单元测试
本程序有三个类Formula,Operand,StuCalculator。针对每一个类写一个测试文件进行测试。总共有90个单元测试用例,总共发现10个BUG。总的代码覆盖率为95.23%,之所以没有达到100%主要是因为,生成表达式时采用的是随机算法,根据不同的随机数会走不同的分支,这在测试中是无法控制的,不能完全覆盖。下附上截图:
代码覆盖率:
5.UML图
6.算法说明:
1.生成表达式算法的整体思路:首先对操作数进行随机生成,对运算符进行随机生成,最后将生成的操作数和运算符拼到一起形成一个前缀表达式,再转换为中缀表达式输出。之所以先生成前缀表达式,是因为要检测生成的表达式是否满足需求,需要进行一步步计算这里因为我们直接生成了前缀表达式,所以可以直接进行计算。
对于去重的检测,我们放弃了检测这一思路,我们的想法是生成的表达式绝不会出现交换重复的情况。为实现这一点,当发现生成的表达式中有‘+’或‘*’时,我们会对操作数做一次排序,就是说是操作数符合有序性,因为交换重复会打破有序性,这样我们保证操作数一定是有序的那么就不会有重复。(这里有一点例外,就是生成的操作数有相同的,所以我们再加一条限制,就是操作数排完序后不允许出现连续三个数相等的情况),操作数有序的做法不能避免产生一模一样的表达式,所以采用哈希值来记录生成的表达式,若出现重复的哈希值则说明生成了一模一样的表达式,则重新生成。
2.对于不同参数组合的支持:这里我们确立了一条规定:允许有负数,就一定允许有括号;不允许有括号,就一定不允许有负数。因为负数输出时都是加括号输出的,所以我们的逻辑导致必须有这条规定。由于我们是随机产生操作数,对于不同的参数就在随机生成的时候根据不同的条件随机生成即可。对于不允许有括号的情况,只要规定随机产生的运算符有序就不需要括号了。
3.对中缀表达式的计算算法:这个没什么创新点,就是两个栈,通过弹栈压栈实现运算即可。