XP简单设计之我见
XP的简单设计指导团队的设计尽可能的简单、具有表现力。此外他们仅仅关注于计划本次迭代中要完成的user story。他们并不会考虑哪些未来的user story。相反,他们会在不断改进系统设计,是之对正在实现的user story而言保持在最优状态。它包含三条原则:
1.考虑能够工作的最简单的事情
尽可能的使用最简单的方法来实现当前的user story。然后选择一种和该方案接近的方案实现该story。在实现story的过程中,如果能使用平面文件就不适用数据库。如果能不适用多线程,就不要使用它。总的意思就是能简单就简单。
2.你将不需要它(You Are Not Going To Need It!)
如果确实需要基础结构,XP团队会对此进行认真的考虑,只有在有证据,或者至少十分明显的迹象表明引入的合理性,团队才会引入这些基础结构。对于以上两条,我个人的理解是关键在于“粒度”的把握,如果我们将一个story的度看成是1的话,那么当我们在遵守以上两条原则的时候,
- 在多大的“度”内进行评估当前的设计是否是最简单的呢?
- 在多大的“度”内进行评估哪些的设计是我所不需要的呢?
- 大家都知道整体最优解与局部最优解的关系,我们将这种关系运用到这里也是非常合适的。假如我们将我们考虑的粒度设置为1的话,也就是我们仅仅将其运用到当前story上。我们看看将会发生什么。1、先写一个测试,该测试体现我们对某个class的期望的行为,体现了我们当前的设计,2、然后我们使该测试通过,3、再接着重构我们的实现;依次一直进行下去,直到我们整个story开发完成。
- 此时我们的工作量大致为: 测试代码(T1) + 实现和重构story的代码(C1)
- 如果当前的story与其他story有较大关联时,或者说他们隶属于同一个context,比如是完成大功能下的小功能模块,那么当我们再去实现那个story时:
- 我们的工作量大致为:测试代码(T2) + 实现和重构story代码(C2) + 重构T1 + 重构C1
- 我们可以看出,在下一个story中我们的实现中我们不仅仅需要重构C1(代码的持续改进,属于正常的工作量),而且需要重构T1的工作量因为我们的设计发生改变。
- 让我们在看一下这个两个story没有什么关联时的情况,
- 这样就非常简单了:测试代码(T3) + 实现和重构story的代码(C3),显然我们的工作量没有额外的增加。
- 如果我们将分析的粒度提高一些,也就是在多个相关(比如两个story)stories之间遵循以上两个原则,
- 我们的工作量:T1* + C1* + T2* + C2*, 在这里我们节省的工作量是重构T1的工作量。
- 大家都知道整体最优解与局部最优解的关系,我们将这种关系运用到这里也是非常合适的。假如我们将我们考虑的粒度设置为1的话,也就是我们仅仅将其运用到当前story上。我们看看将会发生什么。1、先写一个测试,该测试体现我们对某个class的期望的行为,体现了我们当前的设计,2、然后我们使该测试通过,3、再接着重构我们的实现;依次一直进行下去,直到我们整个story开发完成。
- 因此我个人认为,当我们在开发story时,如果能分析一下,我们的story是否是位于一个较大的task之下,或者与其他story有较大关联(根据我实际项目的经验,这是很容易实现的)。我们将以上两个原则与用到它们之间,也就是使我们的分析“粒度”>1,那么我们是可以节省一部分工作量的。
3.一次,并且只有一次
极限编程者不能容忍重复的代码。无论重复的代码出现在哪里,他们都将消除它们。
记得刚刚接触敏捷的时候看到这句话,内心深处可谓是波涛汹涌。没有重复的的代码,天哪那就好比是程序员的共产主义啊(实在是太理想了)。于是乎将之作为理想代码的准则之一,但是经过这么些项目之后,发现现实与理想之间是有差别的。
- 为什么不能存在重复代码呢?
- 在实际的项目交付过程中,我们经常是顶着release的压力写代码,能够顺利的实现手头上的story完成就已经非常不错了。然后再在压力之中进行代码的持续改进。如果按照不能存在重复代码的标准来检验我们的代码,恐怕没有及格的了。其实我个人的感觉就是我们需要找到一个平衡点,在一个合理的范围内进行代码的持续改进,只要我们的代码清晰、易懂、可扩展、可测试,为什么我们不能有一些重复的代码呢?
- 为什么是任何地方都不能出现重复代码呢?
- 好吧,如果我们假定系统中确实是不能有重复代码,但是这样的结果真的是更好的结果吗。我觉得也不尽然。其实人人都知道事情是没有绝对之说的,因此对于“任何”这个词我觉得有些过。
[Observation]
public void shoudl_save_user()
{
var user = new User {Id = 1, Age = 12, Name = "user"};
userRepository.Save(user);
var users = userRepository.Named("user");
users.Count().ShouldBe(1);
}
[Observation]
public void should_delete_user()
{
var user = new User { Age = 12, Id = 1, Name = "user"};
userRepository.Save(user);
userRepository.Delete(user);
var users = userRepository.Named("user");
users.Count().ShouldBe(0);
} - 相信很多人在看到上面这段带码之后,都非常有将黄色重复部分代码消除的冲动(当然还有别的代码)。是的我也非常有这样的冲动,但是我让我们关注在某一个测试用例本身的时候,我们并没有想将这段创建user的代码放到这个测试类的类变量并初始化。因为1.没有重复代码,2.这段代码为我阅读这个测试用例提供了一些context,使我能够很快的获得所有信息,并且知道该测试用例的意图。当然有人说了,这么弱智的测试用例,是个人都能看懂,还需要这样那样的context吗?上面的这段代码只是用于演示的目的,我相信在交付项目中大家肯定遇到过看起来非常痛苦的测试用例,我也看到过。当然这些不是这篇文章的重点。我想说的是,当重复代码为我们带来的好处大于其坏处时,重复代码是可以有的。或者说,重复代码并没有错,错在没有正确使用罢了。
- 好吧,如果我们假定系统中确实是不能有重复代码,但是这样的结果真的是更好的结果吗。我觉得也不尽然。其实人人都知道事情是没有绝对之说的,因此对于“任何”这个词我觉得有些过。
综上,个人认为这些经过很多人验证过的原则对我们的开发工作有着很好的指导意义,但是只有当这些原则与我们自己生产实际情况相结合的时候,才能为真正的为我们创造价值。以上是我个人对XP简单设计的一些看法,希望大家多多指教。:)