设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

跟极限编程创始人Kent Beck学编程

2012-9-28 14:47| 发布者: 红黑魂| 查看: 4389| 评论: 0|来自: CSDN

摘要: 我、Stig、Krzysztof、Jakub和Kent Beck在2012年的Iterate Code Camp花了一个星期时间一起做了个项目,进行最佳实践性的编程学习。我们想和大家分享一下我们在这个过程中学习到的宝贵经验及教训,以使我们能成为更好 ...

行动与要求

我们的Graft数据库有一个接受用户命令的telnet-like接口。参照下面测试addComment的两个简单地变化:

  1. // Test 1  
  2. Graft db = ...; this.subject = new CommandProcessor(db);  
  3. subject.process("addComment eventId my_comment");  
  4.  
  5. assertThat(subject.process("getComments eventId")).isEqualTo("my_comment");  
  6.  // Test 2 (same setUp)  
  7. subject.process("addComment eventId my_comment");  
  8.  
  9. assertThat(db.getComments("eventId")).containsOnly("my_comment"); 

在Test1里面,当对addComment命令进行测试时,直接使用getComments这个命令来检查结果状态。在整个测试过程中,只使用了一个单独的API入口点——subject。Test2是直接访问底层数据库实例并且使用其API来获取数据。与此同时,subject这个API也访问底层数据库。

因此Test1并不是真正意义上的“单元”测试,因为它还依赖另一个测试类。而Test2则更加专注并且写法更加简单,它直接访问目标数据结构,对源代码进行正确检查。

我们将继续讨论像Test1那样的测试,在同等级上,它会完成所有相同的操作,即基于同一级别的公共API对象测试会更好。这里的“更好”是指容易理解、更加重要和稳定可维护,因为它们不是耦合到内部实现的功能测试。

像Test2的这种测试则更常见,无论是直接访问底层(对象、属性、数据库……)还是通过模拟来直接对产生的副作用进行验证。这些技术往往会导致耦合且对测试难于维护,这种技术应该限制在“私有单元测试”中,切勿混合公共和私有单元测试”。

  • 把后面的任务用列表弹出来,而不是立即去做
  • 专注于修复测试——无论多么差劲和简单(或者重构)
  • 专注于当前需求——切勿过早提取

还有一件值得我们的注意的事情,Kent任何时候都会关注自己在做什么。专注意味着你会一心一意的做一件事情,不会受其他事情所影响,无论那件事情有多么重要或者只是简单修复。(备注:永远不要说永远)如果你在修复一个Bug的过程中还发现其他的事情,比如给一个类起更好的名字、删除已死的代码、修复一个未提交的Bug等。这些你都可以用列表记下来,等到手头的Bug修复成功后再去落实。

并行(Parallel)设计

并行设计意味着当改变一个设计的时候,你要尽可能的保持在原来的需求上渐渐添加新元素,直至转向新设计。在这个过程要不断地确保正确性,这需要花费更大的精力,当然这样做也会让其更加安全和容易实现可恢复重构。

一个典型的并行设计案例是使用NoSQL数据库替换RDBMS。开始时,你会把实现好的代码写入新的数据库中,然后再把它同时写进新旧这两个系统里面,并且从新的里面读取数据(也许这样就可以与旧的相比较进行验证),同时还在使用旧的数据库数据。接下来你将开始实际使用NoSQL数据库的数据,同时从旧DB读/写数据(这样你可以方便地切换回来)。只有当新的DB被证明正确无误时,才可以逐步删除旧的DB。

一个小型的并行设计案例是用对象替换方法参数,例如notifuComment方法

  1. - public void notifyComment(String message, String eventName, String user) {  
  2. -    notifications.add(user + ": commented on " + eventName + " " + message);  
  3. ---  
  4. + public void notifyComment(Edge target) {  
  5. +    notifications.add(target.getTo().getId() + ": commented on " + target.getFrom().getId() + " " + target.get("comment")); 

步骤:

  1. 添加Edge做为一个新参数(重构方法)
  2. 通过更换Edge来代替一个个原始参数属性(Infinitest会自动对我们的改变进行验证并且证明是很好的)
  3. 最后删除原始参数

这样做的好处是,代码的正确性会得到保证并且可以随时工作,你可以在任何时候提交或停止。

可恢复的重构

对大规模的代码进行重构时,需要使用一些最佳实践来保证代码随时可被恢复。上面介绍的并行设计是被实践证明的,一种非常安全的重构步骤,在执行过程中不会破坏任何东西,代码的正确性也会得到保证。

在重构过程中,不同的人会有不同的测试策略。但是我想说的,程序员在开发代码时,应该有这样的理念:我因编写代码而得到报酬,而不是为了测试,所以我希望编写的代码能够达到一定水平以至于更少的被测试。(我认为,这种信心等级与工业标准相比还是比较高的,但这仅仅有点狂妄自大)如果我平时很少犯这种错误(比如在构造函数里面设置了错误的变量),就不用对它进行测试。我比较倾向做一些有意义测试错误,所以我会额外关心那种复杂条件下的逻辑结构。当在一个团队中编码时,我会改变测试策略,非常小心的对代码进行测试,最后对错误进行总结。

最后,我通过引用Kent的话:“how much testing to do”来结束这一主题。

对称守则

对称是一个非常抽象的概念,与沟通、简单和灵活相比更加具体,但仍然是普通的。在Kent的《Implementation Patterns》一书中指出,对称应该作为编程原则。

与不对称相比而言,代码的对称性还是比较容易把握的。它更容易去读和理解,所以对称代码会更加具体,再次引用Kent的话:

代码的对称性表现在代码中,所有表达同一想法的地方都用相同的方法来表达。

想象一下代码所要表达的想法,比如“从数据库中获取最后的更新文件”代码需要执行几次。如果方法名称和执行顺序都不同且彼此有很大的差别,那么这样的代码就是不对称的。问问自己:“这个方法是用来干嘛的”。一个对称代码的例子是保持代码在抽象层次上的一致性,比如方法。细心的读者可能已经注意到,一致性是对称的抽象层次,例如一致性的方法命名。但是,对称是抽象的,它涉及更多的想法,而不是规则(如“驼峰式”里的类名和方法命名规则)。

  • 如何管理你的时间,保持精力——注意休息和保持旺盛的体力,在劳累之前必须停下来。一个精力充沛的开发人员比熬夜劳累过度的程序员效率要高很多。(JB Rainsberger在《经济学中的软件设计》中分享过于劳累的工作会使其完全徒劳无功)
  • 结对编程是一种技能,必须自觉地学习(它可能是更具挑战性的一些人格类型,应得到尊重)
  • 与手动更改相比,更喜欢使用IDE重构——f.ex.我们之前从未使用过的“内联”重构,而Kent一直在使用。一旦你掌握了如何重构,你会发现,它明显比手动重构更有效率。更重要的是,避免打破东西(记住, Murphy 所说的——what can break will break)

你可以在GitHub下载我们此次在俱乐部所开发的源码。

总结

我非常希望你们(亲爱的读者)能在平时的工作实践中运用这些价值观和设计原则,希望它们对你有用!

英文原文:Java Code Geeks


酷毙

雷人

鲜花

鸡蛋
1

漂亮

刚表态过的朋友 (1 人)

  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部