结对项目内容:http://www.cnblogs.com/jiel/p/4830912.html
结对成员:康家华,马腾跃(http://www.cnblogs.com/summerMTY)
关于结对编程
“在结对编程模式下,一对程序员肩并肩地、平等地、互补地进行开发工作。两个程序员并排坐在一台电脑前,面对同一个显示器,使用同一个键盘,同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起单元测试,一起集成测试,一起写文档等。”——《构建之法》
通过阅读教科书和网络上的参考资料,我总结了以下结对编程的优点和缺点。
结对编程的优点
- 在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作解决问题的能力更强。
- 对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
- 在企业管理层次上,结对能更有效地交流,相互学习和传递经验,分享知识,能更好地应对人员流动。总之,如果运用得当,结对编程可以取得更高的投出产出比(Return of Investment)。
结对编程的缺点
- 对于有不同习惯的编程人员,可以在起工作会产生麻烦,甚至矛盾。
- 有时候,程序员们会对一个问题各执己见(代码风格可能会是引发技术人员口水战的地方),争吵不休,反而产生重大内耗。
- 两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情,反而分散注意力,导致效率比单人更为低下。
- 结对编程可能让程序员们相互学习得更快。有些时候,学习对方的长外,可能会和程序员们在起滋生不良气氛一样快。比如,合伙应付工作,敷衍项目。
- 面对新手,有经验的老手可能会觉得非常的烦躁。不合适的沟通会导到团队的不和谐。
- 新手在面对有经验的老手时会显得非常的紧张和不安,甚至出现害怕焦虑的的精神状态,从而总是出现低级错误,而老手站在他们后面不停地指责他们导致他们更加紧张,出现恶性循环。最终导致项目进展效率低下,并且团队貌合神离。
- 有经验的人更喜欢单兵作战,找个人来站在他背后看着他可能会让他感到非常的不爽,最终导致编程时受到情绪影响,反而出现反作用。
我的结对伙伴
我很幸运的是能够和同班同学组成结对,这样一来,两人合作的“萌芽阶段”和“磨合阶段”都可以省掉了。
我初次看完我的partner——马腾跃的代码后,我的内心是欣慰的,因为我们两个人在个人项目上的设计想法几乎是一致的,个人编程习惯和风格也十分相似,这就意味着我们在进行结对编程的时候能够很快地在编码规范上达成一致。同时,个人作业的设计相似也为我们在之后的结对项目中可能会有着更好的默契。
任务开始进行到现在,她一直表现得很积极,我们分配的任务她都会按时按点、保质保量地完成,和她合作真的不用担心在deadline之前完不成任务。她写的代码结构非常清晰,该有的注释一个都不少,在结对编程和团队编程中这点是十分宝贵的,因为清晰明了让人一看就懂的代码可以给团队省去很多不必要的时间。而且她还十分的谦虚,她不像那些在编程上有过一些经验就对别人指手画脚、说三道四的“老手”。不管你说的是对是错,她都耐心地听进去,之后再发表自己的意见和看法。跟她在一起编程,让人感觉很轻松,所以那些和她组团的同学们一定要让她在团队中发光发热!
当然没有人是十全十美的,若要在她的身上找些缺点,那我可能要在编程上面找了。她的代码虽然说结构清晰容易理解,但是有一点不足,那就是对于代码的优化方面还需努力。像数据结构上,能用简单的数据结构完成数据的存储是最好的;在一些库函数的巧妙运用要比自己再多写一个函数好更方便;代码上的简化等等。不过这都不是大问题,等经验多了,自然而然的就会养成这些习惯了。
一些设计原则和方法
信息隐藏(Information Hiding)原则
信息隐藏是结构化设计与面向对象设计的基础。在结构化中函数的概念和面向对象的封装思想都来源于信息隐藏。David Parnas在1972年最早提出信息隐藏的观点。他在其论文中指出:代码模块应该采用定义良好的接口来封装,这些模块的内部结构应该是程序员的私有财产,外部是不可见的。信息隐藏原则可以用于一下应用:多层设计中的层与层之间加入接口层;所有类与类之间都通过接口类访问;类的所有数据成员都是private,所有访问都是通过访问函数实现的,这样做确保了数据的安全性。(经过参考书籍和资料整理)
界面设计(Interface Design)
经过查询资料,我找到了如下界面设计原则:
- Clarity is job #1
- Interfaces exist to enable interaction
- Conserve attention at all costs
- Keep users in control
- Direct manipulation is best
- One primary action per screen
- Keep secondary actions secondary
- Provide a natural next step
- Appearance follows behavior
- Consistency matters
- Strong visual hierarchies work best
- Smart organization reduces cognitive load
- Highlight, don‘t determine, with color
- Progressive disclosure
- Help people inline
- A crucial moment: the zero state
- Great design is invisible
- Build on other design disciplines
- Interfaces exist to be used
(Source: http://bokardo.com/principles-of-user-interface-design/)
对于我个人的理解,如果要实现界面设计,首先,要注重第一感觉,也就意味着我们设计的界面要在用户第一眼的时候就知道它的功能和使用方法;其次,实现人机交互,界面的设计要求各有所需,不允许残缺也更不允许冗余,不能让用户在使用的时候感到迷惑;然后,规划界面,包括布局、背景、字体等等,要想用户的喜好上靠拢;最后,检查整体性,没有设计缺陷。
松耦合(Loose Coupling)
一个软件是由多个子程序组装而成,而一个程序由多个模块(方法)构成。 而内聚就是指程序内的各个模块之间的关系紧密程度,耦合就是各个外部程度(子程序)之间的关系紧密程度。耦合是对某个元素与其他元素之间来凝结、感知、和依赖的度量,这里说的元素可以是功能、对象(类),也可以指系统、子系统、模块。它取决于每个模块之间的接口的复杂程度,调用模块的方式——即有哪些信息通过接口。
具体来说耦合就是:元素B是元素A的属性,或者元素A引用了元素B的实例(这包括元素A调用的某个方法,其参数中包含元素B);元素A调用了元素B的方法;元素A直接或间接成为元素B的子类;元素A是接口B的实现。
如何降低耦合:
- 通过应用设计模式实现“低耦合”
- 合理的职责划分
- 使用接口而不是继承
(Source: http://jiadongkai-sina-com.iteye.com/blog/749439)
Design By Contract
契约式设计(Design By Contract)把类和它的客户程序之间的关系看作正式的协议,描述双方的权利和义务。Bertrand Meyer把它称作构建面向对象软件系统方法的核心。
契约式设计的提出,主要基于软件可靠性方面的考虑。可靠性包括正确性和健壮性,正确性指软件按照需求规格执行的能力,健壮性指软件对需求规格中未声明状况的处理能力。健壮性主要与异常处理机制相关 。正确性一方面包括对象元素内部运行的正确性,另一个重要方面是与其它对象元素交互时的正确性。
契约式设计其它的一些好处:
- 文档。在文档中不仅包含签名描述,也能包含契约描述,如果能更进一步象VisualStudio智能提示那样呈现,能减少很多编码过程中犯下错误。
- 测试、调试、品质保证。NUnit是比较完善的断言测试框架,如果语言本身提供契约式设计支持,我们可以使单元测试中断言的覆盖范围更广泛(考虑一般都是开发者自己做单元测试),能够帮助发现更多隐性的缺陷,断言测试代码的编写会更简洁。
这就让我想起了上学期面向对象课程中的规格,包括“Required”“Modified”“Effect”,这种契约式的设计和JAVA中的规格如出一辙,均是面向对象过程中使用的可以保证可靠性的设计方法。
那么如何使用实现契约式设计呢?利用.NET Code Contracts实现运行时验证。.NET的Contract类库是Declarative Programming实践的一部分,可以对日常编程带来很多好处:
- 提高代码可读性,使用者一看Require, Ensure就知道这方法接受什么输入,产生什么输出。
- 减少重复的验证代码
- 配合第三方工具,可以方便静态代码分析和单元测试,方便产生API文档,这些功能可以参见Code Contract主页
Contract类本身已经在.NET 4.0之后集成进了System.Diagnostics.Contracts命名空间,但如果想使用Contract方法实现运行时的验证,还需要单独安装一个VS插件(http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx)。具体的使用方法可以参考http://www.cnblogs.com/polymorphism/p/Using_Runtime_Contract_Checking_In_DotNet_Project.html
(Reference: http://www.cnblogs.com/RicCC/archive/2007/11/28/Design-By-Contract-DBC.html)
设计思想
从构造数据结构来存表达式开始,我们通过递归的方式来储存表达式,比如说(((-5) + (-49)) ÷ (-15)) ÷ 10这个表达式,如果把这个表达式看作是父表达式,那么他就有((-5) + (-49)) ÷ (-15)和10这两个子表达式,再接下去,((-5) + (-49)) ÷ (-15)就有(-5) + (-49)和(-15)这两个子表达式……于是就构成了一个树状的结构,这样一来就有利于存储了。所以我们设计了一个Expression的类来存储表达式。
表达式当中有很多中数字形式,包括整数,分数,带分数还有负数形式,那么如何存储这些数字呢?我们找到这些数字的共同之处,就是它们都能化成分数,所以我们设计了一个Number类,专门用来存储数组,它当中有三个属性:a, b, c(分别表示整数部分、分母和分子),这样一来就方便多了。
按照结对作业的要求,我们设计了三个功能:
- 计算器:输入有效的表达式,计算得到答案;
- 题目生成:根据设定的参数,进行无重复的按照要求的题目的生成;
- 答案检测:给定存有题目和答案的文档,对答案进行正误判断。
我们将核心功能封装到Core类里,相对应的我们设计了四个接口:
- Calculate(),输入表达式string和答案是否显示为小数bool,返回对应的答案
- Produce(),使用这个接口之前,需要运行Setting()函数来设定相应的参数
- Judge(),输入题目文件和答案文件的path和检测精度(0 / 0.1 / 0.001 / 0.0001 / 0.00001),返回判断的信息string
- Setting(),在Produce()之前使用,用于设定produce的各种参数
Calculate(),输入的表达式string进行格式判断,错误则抛出异常。生成Expression,再调用Expression的Calculate函数计算。
Produce(),使用Setting()来设置参数,然后进行生成
Judge(),使用Produce()生成的文件格式的文件来进行答案检测,输入的精度x,允许待测答案和标准答案有[0, x]之间的误差。
Setting(),进行Produce()的参数设置,如果有参数错误的情况,则抛出异常。
总的来说,我觉得我们在数据存储和计算上面比较有特点。在进行查重的工作中我们将表达式按照规则化成字符串,然后和其他的表达式进行比较,如果相同那么就跳过,所以这部分我觉得也挺值得一提的。
使用VS的Unit Test
使用Unit Test来测试Core的接口
覆盖率如下,由于About、Helper和MainFrame是Windows窗体类,在测试的时候没有进行调用,所以未覆盖。Test类是用来启动程序的主类,也未被覆盖。
Core是核心,它的覆盖率是98.74%,Expression类和Number类的覆盖率也达到了99.70%和98.09%,可见我们的测试程序几乎覆盖了整个核心代码区域。
UML图
由于没有找到VS中代码生成UML类图的方法,所以通过使用VS中显示类图的方法生成一个UML图,再通过新建一个UML关系图,进行类的关系连接又生成一个。
结对编程进行中