里面的一些测试技术都很老了,比如junit,现在都注解了.
本来是想跟着里面的电影列表的例子来实践一把的,不过对swing不熟悉,而且没有源代码可以下载的,遇到了一些书面上没有提到的代码,导致中间没法继续下去.
前面几章的一些理论还是不错的,有些以前自己已经知道了,现在再看一遍,有一种顿悟的感觉.
如果能拿java最擅长的web应用做例子就完美了,毕竟做swing应用的人比较少,至少在国内是如此.
写测试的步骤
1.开始先编写一个测试,在测试开头增加一条想让它为true的断言
2.然后为使断言为true创造条件
3.最后需要为创建我们所需要的实际代码.
4.在测试通过之后,开始消除为使测试通过而引入的重复以及其他的一些坏味道代码.
关于重复的一个例子
比如要计算5和3的平均值,我们可以在真实代码中直接返回4,当这个结果通过计算得出结果却返回一个常量时,这就是一种形式的重复.
要去掉重复,首先这样做:
public int getAverage(){return (3+5)/2;}
不过这里仍然存在重复: 3,5与提供给参数是重复的,因此需要通过一个参数用来保存累加的结果:
private int total; public void add(int i){total +=i;} public int getAverage(){return total/2;}
这里因为重构的时候而引入了一个常量2,这也是一种重复,需要通过记录每次累加测试来去掉:
private int number; public void add(int i){total +=i; number ++;} public int getAverage(){return total/number;}
为了对我们的修改增加一些自信,我们可以增加一些新的测试.
在每次小的改动之后运行测试可以给我们信心和保证,其结果有了下一步工作的勇气,每次一小步一小步地推进,劝告那些完美主义者不要一步到位,循序渐进,不要妄想一口吃成胖子.
在消除重复的过程中,我们还有另外一种思路,就是通过再编写一个测试,然后通过重构保证两个测试通过.
但是无论采用哪种思路,都需要编写另外一个测试,这样既可以驱动归纳工作的进行,也可以进一步验证
重构
重构与TDD两个密切相关的两个方面:
尽可能采用简单的方法来让测试获得通过,然后通过重构来对代码进行整理,其中大部分工作是消除那些为使测试通过引入的重复
当采用TDD之后,那么就有了放手重构的安全网.
重构的时机:
当存在重复的时候
当觉得代码或者代码所表达的意图不够清晰的时候
当觉察到代码可能存在坏味道或者有坏味道的倾向的时候
在你要让某个测试通过的时候,带上编码的帽子,一旦测试通过,你就换顶帽子执行重构来进行清理.
关于意图
当我们在编写测试代码的时候,我们会强迫自己考虑类的接口而不是其实现,我们就有机会站在使用这个类的用户的立场来判定什么才是最有意义的,而不是陷入具体的实现细节中去.
code smell
注释:如果你看到一条注释或者觉得有必要编写一条注释的话,那么请首先要考虑重构或重写代码.
数据类:对某个类数据成员的操作处理应该移到该类的内部,而不是将其遗弃在该类之外,让其他的类对其"蹂躏"修改,这个也体现了封装的原则. "上帝的归上帝,凯撒的归凯撒"
交往不当:这种情况是指一个类对另一个类的内部细节知道太多.要处理这种情况,需要对方法进行迁移,让那些彼此了解的代码处于同一位置. 这样一旦这些内部细节发生变化的时候,其影响的范围会仅在该类的内部,而不会出现扩散.
对于继承也会有类似的问题,子类过多的了解祖先类的实现细节,超出它所了解的,这样就应该把继承改成委派(delegation),或者将这种关系松散化,达到去耦的目的.
类尺寸过大:应该采用抽取出子类或者采用多态来减小之
方法过长:一般情况下是完成的工作太多了.
重构手段(手法千千万,常用就这几种)
extract class:当一个类变的太大或者行为逻辑组织分散时,另一种抽取的原因是由于我们需要某种行为的多种实现.
比如电影列表类有一个功能需要输出一个电影列表,那么我们可以将这个功能拆分到一个单独的类中去,这样就能够很容易的进行替换和改进.
extract interface: 记住一点就好,接口最好是小而专.关于接口的命名,尽量采用通过添加-able,-ible结尾的形容词,但是有时候这种命名没有,这可以使用添加前缀I,比如MovieListable没有意义,我们可以采用IMovieList.
extract method: 通过给一块完成某种功能的代码加注释或者通过空行来清晰代码都是一种潜在的坏味道,需要通过提取方法将代码拆分到各自的方法中.
replace type code withsubclasses: 当我们的类使用类型代码来表示子类型的时候,可以使用该手段来打破那些根据类型编码进行判别的复杂条件判断和switch语句.
replace conditional with polymorphism: 当发现switch语句的爱好ihou可以考虑创建子类来处理各种不同的情况. 从而去掉switch.
form template method: 我的最爱,甚至达到滥用的程度:(
introduce explaining variable:当我们的表达式复杂而且难以立即时,我们可以提取其中的某些部分,把中间结果保存在命名清楚的临时变量中.
replace constructor with factory method:当存在多个用于创建类的不同类型的实例的构造方法时,使用起来不是很容易区分,则需要通过使用静态工厂方法,给每个构造方法起一个有意义的名字.
replace inheritance with delegation: 当子类是特殊种类的超类,或者子类对超类进行扩展而不是复写超类的部分功能时,才使用继承.
设计模式应该是重构的目标,应该通过重构逐渐引入设计模式.
如果发现了给参数赋值的代码,那么应该使用临时变量取而代之.
在开始编码的时候,我们应该强迫自己采用最简单的做法.
注释
注释存在的目的是为了掩盖代码的臭味.
正当的注释:
未完成的代码(TODO)
重构尚无法足够清晰
使用了某种非同寻常的算法
使用某种别人发布的算法
性能调整
类注释(解释这个类为什么存在以及用途,要避免编写有关这个类的使用教程)
真正的TDD应该
测试方法应该尽量简短而切中要害
编写一点测试,编写一点代码(test a little,code a little)
不要一口气把所有的测试都写完,然后再编写代码,而要编写一个测试,让其通过,在编写一个测试,再让其运行通过,以此类推.
万事开头难,应该从编写最简单的测试开始.
测试模式
设定前置条件
执行相应的功能
检查后置条件是否满足
TestCase
应该将TestCase视为一种需要需要以完全相同的设置方式来组织测试的方法,而不是用它来组织给定类的测试.
每当你发现setUp中的有些代码使用某些测试方法,而有些代码不使用其他测试方法时,那么就应该认为这个TestCase应该被重构为两个或者多个TestCase.
何谓简单的测试对象:
正确的处理null值
空集或null对象的行为
递归或迭代结构以及递归或迭代计算的基本情况(没明白)
应该尽量使用assertEquals()
尽量在assert中给出相关的message,没有message也是一种bad smell,偶没这个习惯:(
尽量保持测试方法中的断言最少,这样可以保证测试专一,简洁,容易理解
编写测试从assertXxx()开始.
测试代码的数量与应用代码一样多,要是能多上一半就更好了
力争每个测试方法中有只出现一个断言
测试做得好,调试器用的少
TDD事前需要做大量的工作,这个工作的收益主要体现在减少了后期调试程序的工作量
一旦你找到了TDD的感觉,你就会比直接写代码和调试器来进行开发的工作速度快得多.
XP
xp中的四种变化量:成本,时间,质量,范围
其中任何一个的价值依赖于其他三个的价值,你最多只能控制四个中的三个,而绝对无法同时控制四个.
xp的价值观:沟通,简单,反馈,勇气
沟通在xp中具有至高无上的重要性,其外在表现形式:
结对编程
每天早上一次站立会议
在开放的环境工作
客户与开发团队一起工作
大量使用白板
把都感兴趣的信息放在显著的位置
改进设计
在编程的时候,你先编写一个测试(刚刚能够失败就行),然后编写一小段代码(刚刚能够保证测试运行通过就行),这一过程会产生设计或结构低劣的代码,所以下一步就是通过重构来使代码重新恢复渐渐.
极限编程就是让程序员们来编写程序,谋生,并且从中获得乐趣.
要想拥抱变化,我们就需要采用一种渐进的开发方式,每次只改变系统的一小部分,而不是在一次大的交付中搞定所有的一切.
agile model 敏捷建模的两个基本目的:为理解某一问题而建模(诸如如何设计系统的某一部分);为了传达团队正在完成的工作而建模. 敏捷模型大部分都是临时性的,当这些模型完成他们的使命之后,继续留存已经没有价值. 敏捷模型是一种刚刚够好(just good enough)的模型