以前看过《TDD by example》,现在基本都忘记光了,大致记得一切从测试开始,然后不断重构,最后不断的迭代就能得到一个优雅简洁的设计。
关于TDD的很多讨论,我这里就不说了。对于写了那么多年代码的人来说,要想转变观念,还是需要更加冷静,多思考。
TDD 中 kent Beck大师教给我们的是一门内功心法,交给我们的是如何把预想的结果转化为设计。
TDD = Test-Driven Development
很多人以为是讲测试的,其实醉翁之意不在测试,而是开发,如何使代码clean
"TDD by example”这本书的风格我很喜欢,直接从例子开始,让然一看就被吸引住。
书中的例子是一个 货币统计的例子。需要有不同的货币,相加等等。
这时,大家肯定就有一个大概在脑子中浮现。然后很快变开始写代码了。
别急,测试驱动开发中,最重要的就是测试清单了。
测试清单,可以说是半需求吧,我们写一个测试清单,这样,我们要做的事情的目的就有了。
Kent Beck建议我们把清单写出来,然后一个一个考虑。这就是to-do lists
引用
5美元 * 2 = 10 美元
5美元 等于 5美元
单完成一件事,就用横线划掉。
假设上面的清单,第一项,使用测试的风格写出来吧。
@Test public void testTimes(){
Dollar five = new Dollar(5);
five.times(2);
assertEquals(five.amount,10);
}
这个测试显然通不过,因为很多东西都没有,比如Dollar类,times方法
于是我们一一建立起来。直到没有编译错误。
public class Dollar{
public amount;
public Dollar(int amount){
this.amount=amount;
}
public void times(int multipler){
amount*=multipler;
}
上面的代码不是书中第一步的代码,但是我还是走了这么一大步。所以,这里的问题是,如何衡量每一步我们做的改动。在一个实际的或者很复杂的系统中,我们一般是不可能一步到位的,这也是为什么需要测试驱动开发的一个原因吧。但是这里,我真的觉得自己能写到这里。
我还是shamelessly的表现自己很聪明吧。
OK,一切顺利。
接下来我们看看是否可以重构,消除重复,这里我觉得差不多了。那就多些测试吧。
@Test public void testTimes(){
Dollar five = new Dollar(5);
five.times(2);
assertEquals(five.amount,10);
five.times(3);
assertEquals(five.amount,15);
}
这个测试,连续乘,看看是不是会有意想不到的结果呢?实际上,这种测试是常见的边效应测试(side-effect)。
很好,一切与测试驱动开发的教条很符合,我们找到一个缺陷,并编写测试代码使之不通过测试。接下来我们改做些小改动。这里很明显,我们希望美元是一种独立的数字,也就是说它是不可变的。你不可能把币值为5美元变成15美元吧。那么我们在times的时候,就需要重新生产出来一张新的币值为15的美元(可以理解为电子货币,或者支票)。
好吧,先改动测试。
@Test public void testTimes(){
Dollar five = new Dollar(5);
Dollar tenDollar = five.times(2);
assertEquals(tenDollar.amount,10);
Dollar fifteenDollar = five.times(3);
assertEquals(fifteenDollar.amount,15);
}
这时你发现,times返回值是void的,我们修改。
public class Dollar{
public amount;
public Dollar(int amount){
this.amount=amount;
}
public Dollar times(int multipler){
return new Dollar(amount*multipler);
}
}
这下测试通过。然后来看看,我们有没有什么重复的需要重构的。很多时候,我们可能这样写times方法
public Dollar times(int multipler){
int new_amount = amount*multipler);
return new Dollar(new_amount);
}
如果这样的话,我们就可以使用重构中的Inline Method消除这个临时变量。得到之前我们写的times方法。这就是消除重复。
好了,我们终于划掉清单中的第一项了,接下来看第二项:
5美元 确实 应该等于5美元。那就测试测试:
@Test public void testEqualitiy(){
assertTrue((new Dollar(5)).equals((new Dollar(5))));
}
很遗憾,我们失败了。实际上这比较的是两张5美元,虽然币值一样,但是他们确实不同。这不符合我们的意图。于是想起来了,java中需要重写equals方法。但是我们记不清了,重写equals方法有一些需要注意的。所以这里,我先做小的改动,以通过测试。
public class Dollar{
public amount;
public Dollar(int amount){
this.amount=amount;
}
public Dollar times(int multipler){
return new Dollar(amount*multipler);
}
/*
*I recommand you add @Override if you can't
*remember the method clearly
*/
@Override
public boolean equals(Object obj){
return true;
}
}
这里直接返回ture了。肯定很多人要说了,这肯定是有问题嘛。的确如此,但是别急,我没有你那么有经验,问题只能通过测试来反映。测试通过了。看,没问题吧。哈哈。
书中提到一种triangulation。也就是对一个一眼不能看出的问题,可以使用这种方法。
1,找到一种导致测试失败的用例
2,使用stub来实现,也就是一个占位操作,直接返回或是常量。使测试通过。
3,重复1,但是这次可以用真正的写实现代码。似的通过测试。
这里,我们是在3,重复1,找到一个导致这儿假实现的失败。很容易。我们马上就找到了。
@Test public void testEqualitiy(){
assertTrue((new Dollar(5)).equals((new Dollar(5))));
assertFalse((new Dollar(5)).equals((new Dollar(7))));
}
这下,我们不能使用stub来实现了,只能硬着头皮写代码吧。
@Override
public boolean equals(Object obj){
return this.amount == ((Dollar)obj).amount;
}
测试,通过。很好。第二项也就完了,划掉。不过。一切还远远没有结束。
我们考虑下,amount是公有的,这一点看着很不爽,不符合OO的封装性。
测试代码也很乱,很多重复,由于相等性已经测试完成,我们可以重构下测试代码。
再次使用Inline Method方法,重构如下:
@Test public void testTimes(){
Dollar five = new Dollar(5);
assertEquals(five.times(2),new Dollar(10));
assertEquals(five.times(3),new Dollar(15));
}
清爽多了。接下来,把amount私有化改造添加的to-do lists吧。
还有,另外一种货币,Franc来了。我们也也对他像dollar一样测试。
这里,对amount的私有化,似乎很简单了,因为,由于我们在testTimes方法中,使用了对象的比较。所以,当我们修改amount作为私有的时候,似乎一切都没问题了。
分享到:
相关推荐
总的来说,“编程学习笔记”是一个综合性的资源,它将编程的各个方面以易于理解的形式展现出来,无论你是新手还是老手,都能从中找到适合自己的学习路径。通过深入阅读和反复实践,我们都能不断提升自己的编程技能,...
这份"java校招学习笔记"应该是一个全面的参考资料,帮助求职者巩固Java知识,提高解决问题的能力,以应对面试中的各种挑战。同时,实践项目经验和解决实际问题的能力也是校招面试中不可或缺的部分,因此理论学习的...
《达内微软NET学习笔记》是一份详实的.NET软件工程师培训资料,涵盖了.NET框架的基础知识、核心概念以及实际开发中的应用技巧。这份笔记源于作者在加拿大达内科技的培训经历,旨在与学习者共享珍贵的学习心得,由于...
《Rational Rose 2002 学习笔记》是一份深入探讨软件建模工具Rational Rose 2002的教程资料。这份文档详细介绍了Rational Rose在系统分析、设计与实现中的应用,旨在帮助用户掌握这款强大的统一建模语言(UML)工具...
埃迪的TDD学习笔记 :police_car_light: 警告不要合并任何PR。 :memo: 内容所有摘要都放在“问题”选项卡中。 当前,列出了以下内容: NHN FE개발랩摘要-Finsihed 견고한JS소프트웨어만들기-进行中测试Vue.js应用程序...
本篇“C#学习笔记精华”旨在为初学者和进阶者提供一个全面且深入的学习指南,帮助读者掌握C#的核心概念和实用技巧。 1. **基础语法**:C#的基础包括变量、数据类型、常量、运算符、流程控制(如if语句、switch语句...
【C#学习笔记-C# Study Notes】 这是一份详尽的C#学习资源,旨在帮助初学者深入理解和掌握C#编程语言。这份笔记不仅包含了语言基础,还涵盖了从实际问题出发的学习过程,强调理解而非机械记忆,使得学习更加生动且...
### Spring培训学习笔记知识点梳理 #### 一、Spring框架概览 - **启动时间与背景**:Spring项目始于2003年2月,最初的基础代码来源于书籍《Expert One-On-One J2EE Design and Development》。 - **核心特性**: -...
Java学习笔记第四部分主要涵盖了Java编程语言的深入学习内容,可能是继基础语法、面向对象编程之后的高级主题。"良葛格"作为编著者,可能以易懂且实用的方式阐述了这些概念。以下是根据标题和描述可能包含的知识点:...
"Junit良葛格学习笔记"很可能包含了一系列关于如何有效利用JUnit进行测试的教程和示例。下面将详细阐述JUnit的核心概念、功能以及在实际开发中的应用。 1. **JUnit简介**: JUnit是由Ernst Leiss和Kent Beck开发的...
Python 自动化测试开发框架是现代软件开发中的一个重要组成部分,它可以帮助...通过对"python自动化测试开发框架学习笔记"的深入学习,你可以掌握这一领域的关键技术和实践方法,为软件开发的质量保障提供强大支持。
.NET 快速重构学习笔记是针对开发者提升代码质量和可维护性的重要指南。重构是一个系统性的过程,旨在改善软件设计,优化代码结构,而不改变其外在行为。在.NET开发环境中,重构是不可或缺的技能,它可以帮助开发者...
本学习笔记将深入探讨面向对象的基本概念、原则以及常见设计模式。 1. **基本概念** - **对象**:对象是类的实例,具有属性(数据成员)和方法(函数成员)。对象是现实世界中的实体在程序中的抽象。 - **类**:...
AppFuse 是一个开源项目,旨在加速和简化J2EE应用程序的开发流程。由Matt Raible设计,它作为一...通过深入阅读和实践AppFuse的学习笔记,开发者可以逐步掌握其核心功能,并将其应用到实际项目中,提升开发质量和效率。
### UML学习笔记:建模语言详解 #### 统一建模语言(UML)概述 统一建模语言(UML, Unified Modeling Language)是一种被广泛应用于软件开发领域的标准化建模语言,它为软件密集型系统的可视化建模提供了一种统一的...
这个“ssh_web开发测试程序学习笔记”涵盖了使用这三个框架进行Web开发和测试的基本概念、步骤和最佳实践。 Struts2是MVC(Model-View-Controller)设计模式的一个实现,它提供了强大的表单处理、国际化、异常处理...
这份学习笔记聚焦于Python后端开发的知识体系和技术栈,涵盖了Python基础、Django框架、MySQL数据库性能优化、Redis缓存以及面向对象编程等核心内容。 首先,Python是一种高级编程语言,以其简洁清晰的语法和丰富的...
华为作为全球知名的通信设备制造商,其4G模块产品在业界享有很高声誉,因此本学习笔记将重点围绕华为4G模块展开。 首先,让我们来看看4G模块联网的基本流程,这通常由四个关键步骤组成: **STEP1:初始化** 在开始...