2.3 结对编程方法
极限编程的实践中有一个非常重要的原则就是结对编程,这里所谓的结对编程并非是一个人在编程,另一个在看。另外一个人也同样起着非常重要的作用,他需要帮助编码的人找到低级失误,防止其编码出现方向性的错误,特别是在出现一个正在编码的人不擅长解决的问题的时候,他会直接替换编码的人来进行编程。这样做的好处也许只有在实践了之后才能够体会到,它不仅可以避免一些错误的发生,而且可以通过直接的讨论来更快地解决一些容易产生歧义的问题。在交流的过程中,大家的水平也会有很快的提高。结对编程的过程也是一起学习的过程。
2.3.1 什么是结对编程
结对编程(Pair Programming)是一个非常直观的概念,简单地说是指两位程序员肩并肩地坐在同一台计算机前,面对同一台显示器,使用同一个键盘、同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起进行单元测试、集成测试,一起编写文档等。基本上所有的开发环节都面对面、平等、互补地进行开发工作,并且这两人的角色可以随时交换。
结对编程是一个合作式编程模式,是在必要的软件开发环节中,让两名程序员合作来完成同一开发任务。Williams等人把结对编程定义为:“在结对编程中,两名程序员合作开发同一产品模块(设计、算法、代码)。这两名程序员就像是一个联合的智慧的有机体,共同思考问题,负责产品模块的各个方面。一名结对者作为驾驭者(driver),控制鼠标或键盘并编写代码。另一名结对者作为导航者(navigator)主动持续地观察和辅助驾驭者的工作,找出代码的缺陷,思考替换方案,寻找资源和考虑策略性的暗示。结对双方周期性地交换角色。在这个过程的任何时候双方都是平等活跃的参与者,并且不管是一个上午还是整个项目的工作中,双方完全分享所获得的工作成绩。”
目前,有关结对编程的相关理论基本上来自国外。结对编程的概念起源于20世纪90年代。1995年,澳大利亚悉尼理工大学计算机科学教授、国际公认的软件工程理论与实践之人类因素研究权威人士Larry Constantine,在专栏中第一次提到他所观察到的一个现象:“两个程序员一起工作,可以比以往更快地交出完成并经过测试的代码,而且这些代码几乎是没有错误的。”这是结对编程概念的雏形。1996年,由Kent Beck、Ward Cunningham和Ron Jeffries三位软件开发理论与实践极限编程倡导者一同提出了极限编程及它的12个实践,极限编程是由他们开发面向对象软件的经验发展而来的。
结对编程是极限编程的12个主要实践之一,它吸收合作式编程(Collaborative Programming)的关键思想,强调合作和交流。随着敏捷开发思想和极限编程方法在21世纪初前几年的快速普及,结对编程也迅速被大家熟知和尝试。
有很多人声称他们早在多年以前就开始采用结对编程技术了:
- Frederick Brooks是《人月神话》一书的作者。他在给Laurie的一封电子邮件里说道:“我第一次尝试结对编程技术是在我读研究生的时候,我当时的搭档是我的研究生同学Bill Wright。我们一起编写了1500行毫无缺陷的代码,它第一次试运行就通过了。”
- Dick Gabriel是Common Lisp的创始人,也是把模式和模式语言介绍给软件行业的人,他早在20世纪70年代就写出了结对编程技术方面的文章。
? Larry Constantine在他发表于20世纪80年代初的一篇文章里说Withtsmith公司里的“动态搭档”能够以前所未有的速度编写出漏洞更少的代码来。他指出,两位聪明人的头脑,再加上两位互相信任的程序员之间的交流,使代码质量得到了极大的提高。他最终的结论是:让两位程序员合作完成同一项工作绝不是一种浪费,相反,是一条效率更高、质量更优的捷径。 - 在对贝尔实验室的Pasteur项目进行总结研究之后,软件模式运动中最有影响力的人物之一James Coplien于1995年提出了“结对开发”的企业模式。在总结这一模式的时候,他说:“有时候,人们只是在能够得到帮助的情况下才会觉得自己能够解决某个问题。有些问题是任何个人都无力解决的。这一问题的解决方案是让彼此‘兼容’的设计人员结为搭档,他们的合作将比两个人各自独立工作产生更大的效益。采用这种模式的结果是一个效率更高的开发过程。两位搭档要比单独一位开发者犯的错误少得多。”
- 2001年秋,美国北卡罗来纳州立大学的Laurie Williams做了一个实验,该实验主要用来研究下面两个课题:1)结对编程的作用。2)积极协作学习的重要性。Laurie Williams及其助手通过调查学生学习兴趣和学习成绩,发现按照传统的教学模式,很多学生在第二年就会选择别的专业,而放弃计算机科学专业。同时还发现,运用结对编程学习的同学的成绩比独立学习的学生成绩好很多,而且学习兴趣更浓。
- 2002年ThoughtWorks公司的Amr Elssamadisy通过对在项目中实施结对编程进行研究,通过对计划、提交周期、设计的简单性、测试和重构等方面和单独工作进行对比,验证了结对编程的效率比单独编程更高,而且通过一段结对实践,程序员之间能更好地交流,新员工能更快地适应公司环境,对程序员个人提高也更快。同时他对结对编程的实施提出了一些重要意见。
- 2006年美国科罗拉多州刘易斯堡学院Brian Hanks经过调查和实验发现大多数学生对结对编程持欢迎态度,他们认为结对编程可以很好地增进同学之间的关系,更好地加强同学之间的交流,有利于高效地解决难题。
- 2010年南京师范大学计算机学院窦万峰研究小组面向2000多名大一学生的计算机基础课程进行结对学习的实验,发现结对的学生完成作业要比个人单独完成的质量高很多,用时较少,同时他们都表示结对对学习帮助很大。
这些成功的案例促使我们对结对编程实践活动进行研究。在对大量翔实可靠的数据进行研究分析之后,我们得出了这样一个结论:结对编程技术是一种不需要增加多少投入就能够大幅度提高软件产品质量的手段。根据了解的情况,我们相信结对编程技术几乎能与所有的软件开发方法论相结合。
通过大量的实验以及以前的研究表明,结对编程具有如下几个方面的优点:
(1)结对可以最大化地提高工作效率
软件开发并不只是程序员堆砌代码的过程,它更多的是一个创新的过程,是一个发现问题、分析问题、解决问题的过程。一个人编程时,往往有了一丝零碎的想法就开始编写代码。写完代码之后,忽然发现这个方案行不通,只好废弃这些代码,重新开始新的想法。当一个人在遇到疑难问题时,很容易走入“死角”。而结对编程则不同,一个人有了想法,首先要表达出来,让自己的同伴理解,经过深刻的讨论,一致认可之后才开始编写代码。一个人编写代码,另一个人则在旁边思考,会为下一步的工作提出建设性的意见。发现问题可以及时指正,从而提高了代码质量。两个人一起结对,一个人编写代码,另一个人则从设计的角度思考下一步的工作,有了想法之后,互相讨论,再互换角色。在开发过程中,设计思考和编码实现不停地进行交换,保持了良好的开发节奏。结对双方互相督促,使彼此更加认真地工作。遇到问题和压力时,可以一起面对,互相鼓励,同时一起分享解决问题的成就和乐趣。
(2)结对编程可以生成更好的代码
两个人编写的代码总比一个人写的代码好。两个人的智慧确实胜过一个人的,对于影响整个系统的设计决策更是如此。无论一个程序员多么聪明,别人的意见都有助于避免他由于无知、自大或只是疏忽而产生错误决策。虽然许多程序员保持专心致志可能没有问题,但是让其他人使那些普通的程序员不出闪失当然也是有帮助的。当程序员尝试解决困难的问题时,这特别有帮助。当程序员想要放弃时,旁边有人鼓励,从而使他能够继续前进。
(3)结对编程可以减少风险
风险会使大多数团队停滞不前。在软件项目中,管理者不敢做一些有风险的事,结果只是求稳。减少风险的最佳方法是确保团队中的每个人都完全熟悉系统的所有部件以及对系统的所有更改,结对编程可以实现这一点。技术讲解和设计文档很有用,但对于大多数快节奏的项目,它们并不能很好且迅速地传播知识。
(4)结对是知识传播的最好途径
很多软件公司都有自己的知识库,有的还有自己的培训部门,甚至高薪聘请一些专家做技术培训,但发现效果并不理想。培训之后,开发人员面临实际的项目还是感觉茫然。而与有经验的同事一起结对则是在实际项目中学习,具有非常强的针对性。学到的不仅是一些技术和技巧,更多是他们思考问题的方式、解决问题的方法。和各种不同经验的同事一起结对,彼此的经验和能力可以得到快速的提高。
(5)结对可以打造出最佳的合作团队
团队是有组织有计划的,应该合理有效地利用各种资源,进行最佳的组合。结对并不是一对固定的伙伴,我们鼓励在团队中经常交换结对伙伴。这时会发现,项目不再是一个人的事情,也不是两个人的事情,而是整个团队的事情。
通过结对,大家可以在最短的时间内完成磨合。结对能很好地促进团队成员的沟通交流,经常一起合作结对的伙伴,彼此了解、熟悉,很多都是工作和生活上的好友。在这样的团队里,大家很乐意互相协助,一起分享知识,分享快乐。
现在在很多大公司里,越来越多的项目都交给不同地理位置的员工组成的虚拟团队来完成,而且,很多开源软件都是由分布在世界各地的开发者共同完成的,这使得极限编程推崇的结对编程很难应用到这样的虚拟团队。另外,现有的即时通信软件也不能帮助两个开发者共同编辑一个源代码文件,地理位置的限制使得实施结对编程变得几乎不可能。然而有问题就会有办法,为了有效地支持软件分布式开发,提高软件协同开发环境的易用性和有效性,国外软件工程方面的专家提出分布式结对编程的概念,这是对结对编程的探索,从而发挥结对编程在分布式软件开发中的价值。
2.3.2 结对编程分析
1.结对编程与敏捷实践
极限编程的12个实践被划分成3个视角:“编程”视角,描述了编程工作的关注点;“团队活动”视角,描述了团队的一系列实践活动;“交付/管理”视角,描述了团队与客户之间的交互过程。结对编程位于“编程”视角中。我们可以把这个模型看成一个3层结构的洋葱,而这个洋葱的核心是“编程”,它是极限编程推荐的关于编程的4个关注点,即结对编程、测试驱动开发、重构和简单设计。后3个实践在业界一直得到广泛应用,而结对编程没有得到同样的重视。
2.结对编程与测试驱动开发
测试驱动开发(Test Driven Development,TDD)是极限编程的一个重要组成部分,它的基本思想是在明确要开发的功能后,首先完成这个功能测试代码的编写,然后编写相关的代码满足这些测试用例并运行测试;测试通过后继续添加其他功能;循环添加,直至完成全部功能的开发。
为了消除用户需求和业务流程的偏见与误解引起的软件质量问题,需要引进结对编程,将测试驱动开发与结对编程一起使用,结对双方一起对需求进行探讨,并通过互相编写对方的测试案例,一起高质量地通过测试。因此,结对编程对测试驱动开发有非常积极的意义:结对编程是测试驱动开发的双重验证,又是质量控制的有力保证。另外,结对可以使两个人精神更加集中,扩展思路以创造更简单、更严谨、更加有利于测试和修改的代码。
3.结对编程与代码重构
重构就是改进代码的设计,但不会影响外部行为的过程。重构能够简化代码,并能够应对可能出现的任何变化。但是在实施重构时,程序员需要深入考虑如下两个问题。
1)重构的目的是得到好的代码和架构,那么何谓“好的”?目前对这个“好的”还没有明确的标准。在重构过程中,一般情况下我们认为只要是易于修改和扩展的设计或编码,就认为该次重构成功。那么,扩展和修改的最低标准是需要其他程序员可以理解,即重构需要以“具备良好的可读性”为最低标准。而结对可以很容易地使重构满足这一要求。试想如果某个程序员的设计或编码连他的合作伙伴都不能完全理解,那就不能算作是好的设计或编码。实际上,XP的重要性就在于设计或代码朝着易于修改和扩展的方向进行重构,即以所有人都能理解代码为目标实施重构。
2)在XP中一个关键的假设就是“只注重眼前需求的简单设计,而通过重构来适应需求变化”的代价和成本要小于“对系统进行充分详细的设计,但是随着需求的变化设计失效”的代价和成本。而结对编程可以轻松满足这一个假设,因为结对编程对重构的价值在于:我们无法在项目的初期进行一个详细的设计,即使完成一个设计,随着需求的变化,设计也需要频繁地改动。
因此在重构中引入结对编程,可以起到非常重要的作用。结对可以保证重构按照正确的方向发展,是使重构成为一个好的架构或设计的最初保证;结对能够使代码无错误且最简单;结对能够使代码更好地、更有效地重构;结对能够保证重构的及时性,避免单人开发的私有性而导致不愿重构。
4.结对编程与简单设计
简单设计是指以目前的需求而不是未来某些潜在的需求为目标进行设计。通常是采用测试优先的方式进行开发,把设计限制在满足让测试通过的需求下,以使设计保持清晰、简单。
结对编程是简单设计的实际检验。因为即便是最复杂的设计,只要是程序员自己想出来的,他都觉得简单无比,里面充满了直白且显而易见的理由。但是我们要的“简单”,是对项目组里所有人的“简单”。如果他的搭档都不能理解他的设计,那么说明这个设计复杂了;如果两个人都懂,但是交换搭档的时候,新搭档不懂,也说明设计复杂了。结对编程正是检验简单设计的过程。如果认为需求会不断变化,我们的设计需要不断地进行调整、改进,那么结对编程是这种标准最好的保障和实施。
综上所述,结对编程是测试驱动开发、重构、简单设计的有力保证,是防止过度设计的过程。
2.3.3 分布式结对编程
1.面对面结对编程的不足
结对编程是指两个程序员在同一个软件制品上一起工作的软件开发方式。结对编程有许多可以证明的好处,其中包括:高质量的代码和增强了程序员工作的乐趣。遗憾的是,开发人员并不能总是有合适的时间聚集在一起进行结对编程。在这种情况下,他们可以选择不同地域的分布式结对编程。
在结对编程环境中,基础设施缺乏、地理位置分离和时间安排冲突这些障碍经常给结对编程带来困难。分布式结对编程让程序员在不同的地方进行合作编程成为可能。软件行业的一个大趋势就是软件的全球化。这个趋势背后的驱动因素包括软件公司雇用不同城市或国家的高水平程序员,为客户就近成立研究小组,创造快速虚拟发展小组,持续做一些关键性的项目,即使他们不在一个时区也没关系。
2.分布式结对编程概念
近几年,灵活的软件方法在教育和业界中已经引起了越来越多的兴趣,而极限编程被认为是这些灵活工具中最重要的。尽管已经有一些工具能够比较好地支持分布式灵活软件开发,我们仍然有必要对分布式极限编程的工具和处理进行更多的研究,特别是在提供共享编码方式的扩展解决上。鉴于全球化软件发展趋势的继续,要求两名开发者进行面对面的交流并不符合全球化软件发展的需求,这就要求两名程序员虽然在不同的地点,但是他们还能一起合作使用结对编程编写代码,这种方法称为分布协同编程。
分布式结对编程是一种编程风格,两个程序员在地理上是分布的,通过网络在同一个软件制品上同步协作。
3.分布式结对编程优势
Baheti等人测量了生产率(每小时的代码行数)和质量(团队项目等级)。通过统计分析,研究者没有发现分布式和集中式团队在生产率方面的明显差别,但是分布式结对的团队获得了最高的质量。通过对学生的问卷调查分析发现,分布式结对编程可以帮助程序员提高团队协作和交流能力。
Stotts等人研究了来自两所大学的8位学生工作在四个团队来完成一个项目的过程。每个团队都有一个来自不同大学的学生,而且他们从不面对面工作。四个团队中有两个团队进行远程结对编程,另外两个团队被分开,进行单独编程。研究者运行了15个测试用例的集合来对比所有四个团队所完成的项目。两个分布式结对编程的团队都通过了15个测试用例。单独编程的团队中的一个团队通过了12个测试用例,另一个团队没有完成项目。分布式结对编程团队用了较短的平均开发时间。他们认为分布式编程者若在条件允许的情况下应该面对面交流,这可以在他们之间建立非正式关系。如果不能面对面的交流,程序员应该努力在个人层面上相互了解,甚至通过共享个人网页来相互了解。分布式结对编程需要相互间不断地交谈,以便导航者知道驾驭者在做什么。分布式结对编程人员相对大多数同地结对编程人员可能的一个优势,那就是他们每个人都有自己的显示器,这可以提高他们查看代码的能力。
另外,当驾驭者在编写代码的时候,导航者也可以有机会搜索因特网上的相关资源。导航者需要保证遵守结对纪律,以便他不会离开工作。分布式结对编程强制保持其工作的电子副本(例如设计图和说明),以防原文件丢失。当分布式结对编程者学习使用工具和适应远程工作时,他们需要额外的学习时间,这远远超过了集中式结对编程的集结时间。而且,分布式结对编程者可能不得不花费更多的时间口头解释概念和想法,而集中式结对编程者可以只简单地在一页纸上画个草图来解决。
4.分布式结对编程的不足
Ho等人的研究中,来自北卡罗来纳州立大学(NCSU)的四个学生和来自加利福尼亚的四个独立编程人员一起合作开发一个Eclipse插件。他们描述了一些合作时的趣事。首先,他们指出,在分布式结对编程过程中导航者很容易变得烦躁,因为驾驭者不是坐在他的身边,而是他的声音是从计算机的扬声器中发出来的。为了消除这个问题,当驾驭者注意到导航者讲话少时,他就会提醒导航者的工作。其次,他们注意到,分布式结对编程者并没有形成非正式的关系,而集中式结对编程者形成了这种关系。尽管没有一个程序员在该研究之前相互了解,但集中式结对编程者很快就成为朋友。
相对于同地结对编程,学生喜欢进行异地分布式结对编程是因为他们能收到来自同伴好的反馈,在家里进行分布式结对编程方便和舒适,以及减少了时间安排。学生不希望进行分布式结对编程的主要原因是有关分布式结对编程工具方面的技术困难。学生报告说,当他们进行异地结对编程时在开始阶段的生产率并不是很高,这是由于对分布式结对编程工具的掌握需要一个学习过程引起的。学生也列举了时间计划的冲突,以及更喜爱同地结对编程而不是分布式结对编程等其他原因。