论坛首页 综合技术论坛

讨论:重构的前提是不是 TDD

浏览 12719 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2004-07-26  
响应 potian 的意见开始讨论重构。大家来讨论些难度高一点的问题,好让自己有点成就感。
有人看到成功的重构非常依赖于自动测试,于是就产生了这种想法:重构的前提是 TDD,进而产生了这样的推论:因为我们现在没有做 TDD,所以我们不可能做好重构。
我的观点是重构确实很依赖于自动测试,但是重构与 TDD 是两个话题,现在完全采用 TDD 的开发方法只有 XP,但是在其它开发方法中也是完全可以做好重构的。有足够的自动测试是做好重构的有利条件,但是绝对不是是否可以做重构的先决条件。即使没有写很多的自动测试,遇到了臭不可闻的代码,你还是要做重构不是?

我们用 JS 写了不少控件,这些控件如果我现在完全从头写必然要花大量的时间,所以我采用的方法是重构。但是 JS 上面缺乏支持重构的强大工具(例如 Eclipse、IDEA),所以我只能手工来做这件事。而且以前我们从来没有为这些控件写过自动测试(JS 的自动测试也是可以做的),这进一步增加了重构的难度。
这些代码的问题是充满了各种的臭味,除了在当时的项目中可以重用外,几乎不可能跨项目重用。我现在应用各种重构的技巧修改这些代码,使它们逐渐散发出好代码的芳香,并且成为可以跨项目重用的组件,便于实现更大规模的组件化开发。
还有一点就是《重构》中说重构不能改变代码外部观察到的行为,这一点并没有被我当作清规戒律。如果你的代码被很多地方调用,一旦改动外部行为就会使很多人生活很痛苦,那么你当然要小心行事。但是我现在写的这些代码就是我自己带的项目在用,所以不存在这个限制。遇到不合理的地方(比如有个函数有 7、8 个参数)我就直接修改掉。而且我在重构的过程中删剪了一些码又添加了一些新的功能,这和 TDD 中在两顶帽子间频繁切换是类似的。如果不先做重构我就根本搞不清楚哪些代码其实是不需要的,我还有可能在哪些方面做增强。

如同 Martin Fowler 所说,重构不是什么银弹,但是它是一把银钳子。我觉得深入研究好重构比挖空心思写大量注释以求获得更好的理解要有效和重要的多。后者完全是一条死胡同,因此完全不值得大张旗鼓的讨论。
   发表时间:2004-07-26  
如果没有自动测试(与TDD是两回事)。
如果你的两顶帽子在不自觉之间转换。

如果你在修改完代码之后,不是运行测试,而是向天祈祷。
那么你怎么知道,你的“代码修改”,是不是重构呢?
因为重构的基本特征,就是清楚的知道自己“没有把事情搞砸”。
0 请登录后投票
   发表时间:2004-07-26  
庄,除了自动测试,难道就没有其它测试手段了吗?思路太狭窄了吧?
GUI 的测试自动化其实是有很大难度的。比如我做了一个 tree 的控件,我觉得就很难写自动测试(如果你知道不妨告知,我会非常感谢)。但是我做好了,写一个要用到这个 tree 的页面,页面上使用这个 tree 的功能完全正常。我可不可以说这个 tree 的功能就完全正常了?然后我在不改变调用接口的情况下做重构,只要这个页面上的功能一切正常,我是不是就可以说没有改变原有的行为?
自动测试不一定永远比手工测试成本低(你还要想清楚如何写不是吗?如果你想了 3 天才想清楚,那么在这个短的时间内不是还是比手工测试成本高?)。自动测试的好处是这个成本是一次性的,写好以后只要需求不发生改变(谁能保证?)测试就不需要重写,而手工测试下次你还得再重新测一遍。人都是会懈怠的,你让我重新去做一遍与上次相同的测试我可能也会马虎的。

我发这个帖子的意思是,不要认为重构的门槛非常高,好象开始做重构前必须要学一大堆 TDD 的东西,除了 Kent Beck 之流的大师别人没办法效仿。其实重构是非常贴近我们程序员的活动,我们以前可能也经常做,只不过考虑的没有大师那么周到而已。其实每个人都可以从今天开始做重构的。
0 请登录后投票
   发表时间:2004-07-26  
我要说的是,“改善自己的代码”与《重构》是不同的。你可以改善自己的代码,甚至可以参考《重构》中介绍的手法,来改善自己的代码。但是,你的行为不一定能够称之为“重构”。
0 请登录后投票
   发表时间:2004-07-26  
to 庄表伟:
呵呵,是不是有点教条?你以为带一顶帽子就可以解决所有问题吗?在开发过程中重构肯定会与大量的其它行为结合在一起的。但是你要清楚地意识到自己目前是在做哪件事,不要同时做几件事就可以了。
先做起来,然后再去解决遇到的困难比坐而论道好一万倍。
0 请登录后投票
   发表时间:2004-07-26  
为什么需要在重构中使用自动化测试,我想原因大概是因为重构是一种小步骤,高频率的工作。而如果不使用自动化的测试,你的工作无疑可能变得很繁重。而且重构很多时候是一种探索性的,起目标并不是十分明确,所以周期可能会很长,而不依赖自动化测试会很麻烦。不过我确实不觉得我们必须使用自动化的测试,只要我们能承担由此带来的麻烦。
而且现在我们看到很多的工具使一下重构的工作可以变得不依赖测试起来,当然这还会有风险。不过象rename这样的重构,很多时候还是不需要测试的配合的。但是从整体看测试是使重构安全进行的必要条件。
但是我想修改活动总是伴随者我们的开发活动的,虽然有少数一次想好,一次成型的天才,但是绝大多数人还是要不断的修改。而重构作为一种单纯的只是针对结构的而非功能的修改,起意义使巨大而又难于琢磨的——究竟什么时候应该开始,什么时候应该结束,这个问题很难有一个客观的标准,味道也很难进行判定。同时就如同任何设计一样,重构也存在多样性。就是这些多样的选择,让你很难在确定你到底做了什么,影响到了什么。而作为一种安全网,测试的完备无疑是必要的。
同时修改往往还会改变起行为,而重构往往是为这个改变进行准备的工作,只不过重构自己不去改变。很多时候行为改变是必要的,所有为此改变进行的准备也是必要的,而知道自己究竟在干什么也是必要的。
0 请登录后投票
   发表时间:2004-07-26  
我比较同意dlee的意见。

重构我都是从广义上来理解的,只要是涉及到对已有的代码进行修改,都可以唤作“广义重构”。

还有一个需要考虑的是测试的“代价”,测试是一定是需要付出代价的,根据测试的难度、严密度和覆盖度,测试的代价有可能会相差很大。

个人觉得:像代码“味道”,“整洁”等是非常感观的,很多时候凭的是当时的个人感受(心里是否有底),而觉得是否需要对其进行重构。如果没有自动化测试来保证代码的正确性,很有可能自己对重构后代码没有把握,其实这里面也有一个自信心的问题。我的原则是:

1、对有复用价值的代码一定要进行重构
2、对关键性、自个心里没底的代码重构,一定要有严密的测试
3、对显示性(GUI)、心里比较有底的代码重构,可以用一些土办法(土办法也是测试,只是严密度和覆盖度会不同)

实际运用中,有一些代码是很难测试的,尤其是自动化测试,如:js。这时就可以灵活机变,可以通过自个经验采取一些土办法。一般的项目中,不会有那么多时间供你来调配。

最重要的是不能被一些框框束缚住自己的手脚,根据实际情况和个人经验,该怎么干就怎么干。
0 请登录后投票
   发表时间:2004-07-26  
庄表伟 写道
我要说的是,“改善自己的代码”与《重构》是不同的。你可以改善自己的代码,甚至可以参考《重构》中介绍的手法,来改善自己的代码。但是,你的行为不一定能够称之为“重构”。

     这点,我非常赞成。我觉得,重构的前提是会影响到他人的工作或者整体的时候,才能称之为重构。重构是需要进行回归测试的。
   个人编码期间,调试的时候,对自己代码进行整理修改,这都不能称为重构,因为它都没有进入单元测试阶段,都没有正式提交。它只是用到了重构的方法罢了。
0 请登录后投票
   发表时间:2004-07-26  
GUI 部分因为经常处于变化之中,是否一定要采用 TDD 方式做开发还值得商榷。有时候你们写了大量的测试,结果客户要求你们改为另外一种风格,那么不光功能代码要改写,测试代码也会变的无用。所以上周日和 o6z 兄聊天时他说他认为 GUI 部分就不应该做自动测试。这个显然是从实用角度做出的结论,如果一定要强调 xx 大师如何说,反而束缚住了自己的手脚。正是因为这个原因,两年前在我参加的那个 XP 团队中用 HttpUnit 做页面自动测试时,写的都是些比较简单的测试,基本上只测试了页面上链接的可达性,而并没有做很复杂的测试。那种很复杂的页面自动测试如何写可能大家都不是很清楚。另外 GUI 部分还涉及到交互设计、人机界面等方面的问题,这些直接与用户使用感受有关的内容自动测试是根本无法涉及到的。国外的大公司一般是专门找一些典型用户,支付他们报酬来做这类测试。我即使写了一个惨不忍睹的页面,只要实现了功能,照样可以通过自动测试,但是这个页面是用户可以接受的吗?

其实重构的那些技巧适用范围是非常广的,在很多场合都可以用到。我更愿意接受一种广义的重构概念。至于别人是否认为这就是重构,那是和我毫无关系的事情。
0 请登录后投票
   发表时间:2004-07-26  
引用

我发这个帖子的意思是,不要认为重构的门槛非常高,好象开始做重构前必须要学一大堆 TDD 的东西,除了 Kent Beck 之流的大师别人没办法效仿。其实重构是非常贴近我们程序员的活动,我们以前可能也经常做,只不过考虑的没有大师那么周到而已。其实每个人都可以从今天开始做重构的。

我基本上同意Dlee的观点,也就是gigix的观点,也是KentBeck的观点,这只不过是一种习惯而已。


后山说:
引用

1、对有复用价值的代码一定要进行重构

很多时候,一开始其实你是不知道哪些代码可以复用的,如果你再经过几次重构以后突然在代码中发现可以重用的部分,可以合并的类层次,那才说明重构本身就是设计的一种工具。

至于什么时候要重构,很多时候确实是基于一种感觉(所谓的味道),但最直接的因素应该是理解出现困难、新的代码难以加入时和review的时候


有没有自动测试的算不算重构,我想只要是意图上不想改变外部行为的都应该称之为重构。现在可以“基本保证“外部行为不变的只有三种方法:

1。数学证明
2。工具支持
3。单元测试

每做一个重构步骤都用数学去证明一下不太现实,即使这样,我们也难免不再手工操作的过程中不小心违反了证明的一些步骤和基本前提。所以我们最好是有工具支持,但工具支持也不能完全保证(譬如eclipse想要重命名子包名,而不想重命另一个源代码目录下的相同包名,)工具也只能提供有限的重构方法,单元测试只能在你测到的范围之内,也就是你已经测试的外部行为上保证正确。

因此,我认为dlee提出的不进行自动测试进行重构,用手工测试,应该也是一种重构。

但是,我不同意Dlee的边改边重构,因为重构的基本定义就是不改变外部行为改良内部结构。当然这个外部行为的范围是不一定的(例如对某个子模块是改变的,但对整个系统是不变的),但至少我们应该保证不增加新的行为。我最喜欢的一个比方就是造房子,重构就是原先我们有一个5层楼的地基,现在要造10层楼,我们首先应该把地基加深到可以造6曾、7曾、8层、10层,这个时候往上造(增加功能),就比较牢靠了。
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics