preserve改装(整洁的代码,再见)_重复

作者|DanAbramov译者|弯月,责编|屠敏以下为译文:深夜。同事刚刚提交了他们编写了一整周的代码。我们在编写一个图形编辑器,他们实现了通过拖动矩形和椭圆的边缘来调整图形大小的功能。他们的代码运行良好。但代码中有重复。每种形状(如矩形或...

整洁的代码,再见

作者|DanAbramov

preserve改装(整洁的代码,再见)_重复

译者|弯月,责编|屠敏

以下为译文:

深夜。

同事刚刚提交了他们编写了一整周的代码。我们在编写一个图形编辑器,他们实现了通过拖动矩形和椭圆的边缘来调整图形大小的功能。

他们的代码运行良好。

但代码中有重复。每种形状(如矩形或椭圆形)都有一组不同的拖动点,而且沿不同方向拖动也会以不同的方式影响形状的位置和大小。如果用户按住Shift键,我们还需要在调整大小时保持比例。这其中涉及很多数学运算。

他们的代码如下:

letRectangle={

resizeTopLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeTopRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeBottomLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeBottomRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

};

letOval={

resizeLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeTop(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeBottom(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

};

letHeader={

resizeLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

}

letTextBlock={

resizeTopLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeTopRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeBottomLeft(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

resizeBottomRight(position,size,preserveAspect,dx,dy){

//10repetitivelinesofmath

},

};

这些重复的数学计算看着真碍眼。

这种代码不整洁。

大部分重复都发生在相似方向的拖动。例如,Oval.resizeLeft与Header.resizeLeft很相似。这是因为这两个方法都是向左拖动。

另一个相似之处出现在同一个形状的方法之间。例如,Oval.resizeLeft与Oval的其他方法都很相似。这是因为它们都是处理椭圆的方法。Rectangle、Header和TextBlock之间也有一些重复,因为文本块都是矩形的。

突然,我想到了一个好主意。

我们可以像下面这样将代码分组,然后就可以删除所有的重复代码:

letDirections={

top(...){

//5uniquelinesofmath

},

left(...){

//5uniquelinesofmath

},

bottom(...){

//5uniquelinesofmath

},

right(...){

//5uniquelinesofmath

},

};

letShapes={

Oval(...){

//5uniquelinesofmath

},

Rectangle(...){

//5uniquelinesofmath

},

}

然后是它们的行为:

let{top,bottom,left,right}=Directions;

functioncreateHandle(directions){

//20linesofcode

}

letfourCorners=[

createHandle([top,left]),

createHandle([top,right]),

createHandle([bottom,left]),

createHandle([bottom,right]),

];

letfourSides=[

createHandle([top]),

createHandle([left]),

createHandle([right]),

createHandle([bottom]),

];

lettwoSides=[

createHandle([left]),

createHandle([right]),

];

functioncreateBox(shape,handles){

//20linesofcode

}

letRectangle=createBox(Shapes.Rectangle,fourCorners);

letOval=createBox(Shapes.Oval,fourSides);

letHeader=createBox(Shapes.Rectangle,twoSides);

letTextBox=createBox(Shapes.Rectangle,fourCorners);

改完以后,代码行数只有原来的一半,重复完全消失了!好整洁的代码!如果我们想更改某个方向的行为或某个形状的行为,则只需要修改一个地方即可,不像原来的代码需要修改所有类似的方法。

修改完代码后我才发现已经很晚了,我完全忘记了时间。提交完重构后的代码后,我心满意足地上床睡觉了,内心因为帮助同事整理代码而充满了自豪。

整洁的代码,再见

第二天早上

情况与我预想的完全不一样。

老板找我去谈话,他们很客气地要求我把代码改回去。我吓傻了。原来的代码一团糟,而我的代码非常整洁!

我很不情愿地答应了,但是直至几年后,我才幡然醒悟:他们是正确的。

整洁的代码,再见

这是每个人都经历过的一个阶段

相信每位痴迷于“整洁的代码”,希望消除重复的人都经历过这样一个阶段。当我们对代码不自信时,很容易将自我的价值感和专业自豪感附加到某种可以衡量的事物上:一组严格的lint规则、命名架构、文件结构、没有重复代码。

你无法自动删除重复代码,但是通过练习确实很容易实现。通常,在修改完代码后,你很容易看出代码行数是增加了还是减少了。于是,消灭重复代码就成了改善代码的客观指标。更糟糕的是,它混淆了人们的认同感:“我就是那种编写整洁代码的人”。这是一种强大的自我欺骗。

在我们学习了如何创建抽象后,很容易痴迷于这种方式,每当看到重复的代码时,就忍不住想提取抽象。在积累了若干年的编程经验后,我们看到重复无处不在,而抽象就成了“超能力”。如果有人告诉我们抽象是一种美德,我们就会全盘接受。而且我们还会开始评判其他人是否不崇尚“整洁”。

回头再看当年我的“重构”完全是一场灾难,原因主要有两个方面:

首先,我没有和编写这段代码的人沟通。我重写了代码,并提交了代码,而他们却毫不知情。即便这是一种改进(虽然我不觉得),但这也是一种很糟糕的解决问题的方法。一个健康的工程团队需要不断地加强信任。不经讨论就重写队友的代码,这会重重地打击团队的协作能力。

其次,天底下没有免费的午餐。我的代码虽然消除了重复,却以改变需求作为代价,而且这笔买卖并不划算。例如,之后我们需要处理针对不同的形状的不同操作,而且还有很多特殊情况。在我的抽象代码中实现这样的变更必须付出数倍的努力,而在原来那个“凌乱”版本中进行此类修改就非常容易。

那么,你应该编写“脏乱”的代码吗?当然不应该。我建议你仔细思考“整洁”和“脏乱”的含义。你有什么感觉?革命?正义?美丽?优雅?你如何确定这种质量会带来怎样的结果?而它们又会对编写和修改代码的方式造成怎样的影响?

我相信很多人都没有仔细思考过这些问题。我曾经深思过代码的外观问题,但是没有考虑过为什么他们会采用这种方式编写这些代码。

编程是一次旅行。想一想从第一行代码到如今,你走过了多少崎岖。当第一次通过提取函数或重构类将复杂的代码简单化时,我的内心也非常高兴。如果你对自己的代码感到自豪,那么也很容易抵制不住诱惑追求代码的整洁性。你可以进行一些尝试。

但你所做的努力不能仅限于此。不要成为一个执着于追求代码整洁的人。整洁的代码不是目标。这只是我们为降低系统巨大的复杂性而做的尝试。如果你不确定你的修改对代码库会造成怎样的影响,但你需要从千头万绪找到一个切入点,那么“整洁的代码”可以成为你的方向指导,但你必须学会及时放手。

原文:https://overreacted.io/goodbye-clean-code/

本文为CSDN翻译,转载请注明来源出处。