错误的抽象

这篇文章本来是我在Chainline Newsletter写的,不过后来我经常在推特上收到相关内容的@,所以我现在在我的博客上重新发布这篇文章,内容略有修改。

我一直在思考“错误的抽象”所带来的结果,我在RailsConf2014“细枝末节”的演讲中有一部分说道:

重复是比错误的抽象更低成本的。

在摘要中,我也建议道:

建议使用重复而不是错误的抽象。

这个长篇演讲中的小插曲却一石激起千层浪,引起了强烈的反响。少数人觉得我糊涂了,但是更多的人对下面这句表示认同:

这些,无数次的这些情况!很多条都像如下这条这么说!“@BonzoESC:”重复是比错误的抽象更低成本的“ @sandimetz @rbonales pic.twitter.com/3qMI0waqWb”

-41个蓝色调(@pims)2014.3.7

这些反应的力量使我意识到“错误的抽象”所带来的问题是如此普遍和棘手,下文是我提出的几个问题:

1、程序员A发现了重复。

2、程序员A抽取了重复部分并给它命名。

这就创造了一个新的抽象,它可以是一个新的方法,甚至也可能是一个新的类。

3、程序员A用这个新的抽象代替了重复。

吼!这段代码棒极了,程序员A欢快的跑了起来。

4、时光流逝。

5、针对目前这个近乎完美的抽象出现了一个新的需求。

6、程序员B被分配了实现这个需求的任务。

程序员B认为保留这些原来的抽象是荣誉攸关的,但是因为每种情况都不太一样,只能修改这些代码来接收参数,然后根据这些参数的值再加上执行逻辑的条件式来得到正确的结果。

一个曾经通用的抽象现在却需要根据不同情况做出不同表现。

7、一个新的需求出现。
程序员X。
又增加一个参数。
又增加一个条件式。
循环往复直到代码不可维护。

8、此时你在这个故事中登场了,然后你的生活戏剧性的急转直下。

现有的代码有很强的影响力,其存在就说明它的正确性和必要性。这些代码代表了我们的努力,所以我们有很强的动机保卫这些努力的价值。但不幸的结局是这些代码越复杂越难懂,也就是说,创造这些代码的投资越多,越让我们感到维护它很有压力(这就是“沉没成本误区”)。犹如潜意识告诉我们:“神啊,这太难懂了,肯定调试了很久才调好吧。这段代码肯定很有用,不能让这些努力白费掉。”

当你在如上这个故事的第八步出现时,这种压力可能会迫使你继续前进,也就是通过修改现有的代码来完成新的需求。然而尝试这样做是不理性的。这些代码不再代表一个纯粹的、通用的抽象,而是一个充满附加条件的过程,其中夹杂了一系列错综复杂的目的,令这代码既难理解又容易出问题。

如果发现自己处于这种情况下,要坚决拒绝被沉没成本所驱使。当处理这些错误的抽象时,最快的前进道路是回头,照着下面的步骤来做:

1、把嵌入在抽象代码中的重复部分再次引入到每个调用它的地方。
2、在每个调用内部,用被传递的参数来决定保留哪一部分inlined代码会被这个调用所执
3、删掉对每个不同调用来说不必要的部分。

这样就同时移除了抽象和条件式,并且减少了每个调用的代码,只保留它所必要的。当你回过头来看使用这种方法的决定时,通常会发现,即使每个调用表面上调用了一个共享的抽象,但是每段代码平等且独立的运行着。一旦完成了对旧的抽象的移除,你就可以重新开始了。重新对重复隔离,然后重新提取抽象。

我看过很多人试图勇敢的带着错误的抽象前行而出现很多问题,且鲜有成功。新增特性是非常困难的,并且每一次成功新增都会使代码更加复杂,也使下一次新增特性变得更加困难。当人们改变他们对“必须保留我们在这段代码中耗费的心血”的想法,转而成为“这段代码的意义是暂时的,可能我们已经掌握了所有我们能从它中所学到的东西”,让自己根据目前的需求重新思考这些抽象时,事情就变得简单了。当他们内嵌这些代码时,前面的路就清晰了,增加新特性也变得简单高效。

这个故事的意义是什么?不要被沉没成本误区所困。如果你发现自己在共享代码中传递参数、增加条件分支时,这个抽象就是错误的。也许开始时它是正确的,但是那一天已经过去了。当一个抽象被证明是错误的时候,最佳策略是重新引入重复,让它告诉你什么是正确的。即使累积条件式偶尔有效,能让你深入了解这是怎么回事,但如果你越快放弃这些错误的抽象,你将会越少承受它所带来的痛苦。

当抽象是错误的时候,最快的前行道路是回头,这不是倒退,而是向更好的方向进发。就这样做!将会使你和后人的生活更美好。

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

2 1 收藏 评论

关于作者:欣仔

假装会写代码的伪程序员~ 个人主页 · 我的文章 · 12 ·   

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部