错误的抽象


复制比错误的抽象便宜得多(代价小成本低),宁可重复而不选择错误的抽象。
让人们意识到“错误的抽象”这个问题是很难:

  • 程序员A看到重复。
  • 程序员A提取重复并为其命名。
    这创建了一个新的抽象。它可能是一种新方法,甚至可能是一种新类。
  • 程序员A用新的抽象替换了复制。
    啊,代码很完美。程序员A快乐地走开了。
  • 时间流逝....
  • 出现了一个新的需求,当前的抽象几乎是完美的。
  • 程序员B负责实现此需求。
    程序员B感到很荣幸能够保留现有的抽象,但由于每种情况都不完全相同,他们会改变代码来获取参数,然后添加逻辑以根据参数的值有条件地做正确的事情。
    曾经的通用抽象现在对不同的情况有不同表现了。
  • 另一个新需求来了。程序员X. 再另加一个附加参数和另一个新条件,如此循环直到代码变得难以理解。
  • 你出现在这里的故事中,你的生活发生了戏剧性的转折。

现有代码会产生强大的影响力。它存在的本身会被认为它既正确又必要。我们知道代码代表了努力的付出,我们非常积极地保持这种努力的价值。
不幸的是,令人遗憾的事实是,代码越复杂和难以理解,即创建它的投入越深,我们就越感到继续保留它会产生很大压力。(“ 沉没成本谬误 ”)
这种压力可能迫使您继续前进,即通过更改现有代码来实现新要求。然而,试图这样做是残酷的。代码不再代表单一的,共同的抽象,而是成为一个条件负载的过程,它交织了许多模糊相关的想法。它很难理解,也很容易破碎。
如果你发现自己处于这种状况,就要抵制沉没成本的驱使。在处理错误的抽象时,最快的前进方法就是返回。请执行下列操作:
  • 将抽象代码放回每个调用者,重新引入重复的代码。
  • 在每个调用者中,使用传递的参数来确定此特定调用者执行的内联代码的子集。

这将删除抽象和条件,并将每个调用者减少到它们只需要的代码。
过去的代码虽然每个调用者表面上都调用了共享抽象,但他们运行的代码却相当独特。一旦完全删除旧的抽象,您就可以重新开始,重新隔离重复并重新提取抽象。

人们正在勇敢地尝试以错误的抽象方式前进,但收效甚微。添加新功能非常困难,每次成功都会使代码变得更加复杂,这使得添加下一个功能变得更加困难。

当他们改变他们的观点时,“我必须保持对这段代码的投资”,“这段代码有一段时间才有意义,但也许我们已经从中学到了所有东西”,并允许自己重新思考根据当前的要求抽象,一切都变得容易。一旦他们内联代码,前进的道路变得明显,添加新功能变得更快更容易。

如果您发现自己传递参数并通过共享代码添加条件路由时,则抽象是不正确的。
(banq注:抽象出的代码里如果有if前置条件判断,说明逻辑的默认前置条件被破坏,抽象是错误的)

一旦抽象被证明是错误的,最好的策略是重新引入重复并让它向您展示什么是正确的。