文档章节

【译】和整洁代码说再见

善良超哥哥
 善良超哥哥
发布于 01/19 19:37
字数 2478
阅读 2W
收藏 5

本文是我在 Hacker News 上看到的一篇文章。原文讲述了作者对整洁代码的一些思考。本人在学生时期看过一些关于代码风格的书,比如《高质量程序设计指南》、《代码大全》、《代码整洁之道》等等。也养成了自己对优雅、简洁代码的偏执,相信很多程序员小伙伴都有这种偏执。但工作后,随着代码经验的积累和同事的打脸,开始反思自己坚持的风格。偶然看到 Hacker News 上的这篇文章,和作者有点感同身受,情不自禁想把这篇文章分享给大家。

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

原文在 Hacker News 上引发的讨论:https://news.ycombinator.com/item?id=22022466

以下是译文。

那是一个深夜。

我的同事们刚刚提交了他们花了整整一周编写的代码。我们的需求是在图形编辑器画布上工作,同事们刚提交的代码实现了通过拖动图形边缘的小手柄来调整矩形、椭圆等形状。

代码工作良好。但是有重复代码。每个形状(例如矩形和椭圆)都有一组不同的手柄,在不同的方向上拖动每个手柄,会以不同的改变形状的位置和大小。如果用户按住 Shift 键,我们还需要在调整形状大小时保持原来的比例。这里面有一堆数学问题。

代码大概是这样的:

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
};

let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
};

let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },  
}

let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 这里省略 10 行重复的数学代码
  },
};

这些重复出现的数学运算让我心烦。一点都不整洁。

大多数重复代码都发生在相似的方向上。例如,Oval.resizeLeft()Header.resizeLeft() 的代码类似。因为它们都要处理左边的拖拽操作。

另外,同一形状的不同方法也有相似之处。例如,Oval.resizeLeft()Oval 的其他方法也有相似之处。因为它们的处理对象都是椭圆。矩形、HeaderTextBlock(文本块) 也有重复的地方,因为文本块也是矩形。

我想到了一个好主意。我们可以通过以下代码分组来消除所有的重复:

let Directions = {
  top(...) {
    // 这里省略 5 行独特的数学代码
  },
  left(...) {
    // 这里省略 5 行独特的数学代码
  },
  bottom(...) {
    // 这里省略 5 行独特的数学代码
  },
  right(...) {
    // 这里省略 5 行独特的数学代码
  },
};

let Shapes = {
  Oval(...) {
    // 这里省略 5 行独特的数学代码
  },
  Rectangle(...) {
    // 这里省略 5 行独特的数学代码
  },
}

然后组合它们:

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

function createHandle(directions) {
  // 这里省略20行代码
}

let fourCorners = [
  createHandle([top, left]),
  createHandle([top, right]),
  createHandle([bottom, left]),
  createHandle([bottom, right]),
];
let fourSides = [
  createHandle([top]),
  createHandle([left]),
  createHandle([right]),
  createHandle([bottom]),
];
let twoSides = [
  createHandle([left]),
  createHandle([right]),
];

function createBox(shape, handles) {
  // 这里省略20行代码
}

let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

代码量减少了一半,完全没有重复代码!很整洁。如果想要在某个特定方向或针对某个特定形状做修改,只需要修改一个对应的方法,而不需要修改每个形状的对应的方法或者某个形状所有的方法。

夜已经很深了。我向 master 分支提交了我重构后的代码,然后就上床睡觉,我为自己解决了同事杂乱的代码而感到自豪。

第二天早晨

...没有发生我预期的剧情(为什么没人夸我)。

老板找我进行了一对一的谈话,他们(老板和同事)很礼貌的要求我撤销我提交的代码更改。我惊呆了。旧代码一团糟,我的代码多整洁!

我勉强答应了,但是过了几年我才发现他们是对的。

一个必经阶段

痴迷于“整洁的代码”和删除重复代码是我们很多人都会经历的一个阶段。当我们对自己的代码不自信时,很容易将自我价值和职业自豪感与一些可以衡量的东西关联起来。比如,一套严格的 lint 规则,一个命名模式,一个文件结构,消除重复等等。

译者注:Lint 指一些列静态代码分析工具,能够发现代码中潜在的缺陷和质量问题。

你不能自动删除重复代码,但通过实践会变得越来越容易。你通常可以分辨每次更改后代码是多了还是少了。因此,删除重复代码感觉就像提高了某些和代码相关的客观指标。更糟糕的是,它与我们的认同感交织在一起:“我就是那种编写简洁代码的人”。这种认同感和任何形式的自我欺骗一样强大。

一旦我们学会了如何创建抽象,就很容易对这种能力产生很高的期望,并且每当我们看到重复代码时就会凭空想象出抽象。写几年代码后,我们发现到处都是抽象——抽象就是我们新的超能力。如果有人告诉我们抽象是一种美德,我们就会全盘接受,并且会批评那些不崇尚“整洁”的人。

现在我发现我之前的“重构”在两方面做的很糟糕:

  • 首先,我没有和写代码的人沟通过。没有了解他们的想法和意图就自己重写了代码并提交。即使我提交的代码是一种进步(我现在不信了),这也是种糟糕的方式。一个健康的工程团队需要不断建立信任。在没有沟通的情况下重写队友的代码,极大的降低你在代码库中和队友有效协作的能力。
  • 其次,任何事都有代价。我的代码减少了重复代码,但牺牲了应对需要变更的能力,明显不划算。例如,我们后续需要为不同的形状上不同的句柄添加很多特殊的情况和行为。我的抽象代码必须变得复杂好几倍才能应对这种需求,而在最初“混乱”的版本中,这种更改就像切蛋糕一样简单。

我是在说你必须写“脏”的代码吗?不,我是在建议你认真思考,当我们讨论“整洁”和“脏”时,我们到底是在讨论什么。你有反抗的感觉吗?正义?美丽?优雅?你确定你能列出和这些品质相对应的具体工程成果吗?它们究竟佮如何影响了我们编写和修改代码的方式?

我之前确实没有深入思考过这些东西。我对代码的外表思考了很多——但没有考虑它是如何在一群糊涂的人手中演变出来的。

写代码就像一段旅程。想想你从第一行代码到现在已经走了多远。我理解第一次看到如何提取一个函数或重构一个类可以简化复杂的代码时是多么有趣。如果你对自己的手艺感到自豪,追求诱人的整洁代码。你可以逗留一段时间。

但是不要止步于此。不要做一个整洁代码的狂热分子。整洁的代码不是目标。整洁的代码只是我们处理极其复杂的系统时的一种尝试。当你不确定更改将对代码库造成什么样的影响时,整洁的代码风格是一种防御机制,指导我们在未知的海域航行。

让整洁代码风格指导你。该忘记的时候就忘记吧。

译者后记:翻译完这篇文章,发现原作者的思维有点跳跃。从一次擅改代码被打脸,到后面关于整洁代码一大段思考,中间过了几年时间,这几年时间作者肯定还经历很多其他事情,这些事情加在一起才能得出本文后面一大段思考和结论。不知道你有没有类似的经历和感悟,欢迎讨论。

如果习惯在微信公众号上阅读,欢迎关注我的公众号:不只是Python 以后会继续在开源中国和微信公众号上同步更新文章 微信公众号:不只是Python

© 著作权归作者所有

善良超哥哥
粉丝 4
博文 1
码字总数 2478
作品 0
长宁
私信 提问
加载中

评论(4)

x未央
x未央
我从来不要求没bug,只要求出现bug好找。
行者__
行者__
阴阳之道 随机应变 , 这个随着功力深厚, 可以较好的选择 驾驭 不同风格的代码
古科明
哈哈。一般能跑的代码最好不要改。改出屎了还要负责。
那种非常优雅的代码只能看欧美的一些开源软件了。简直像教科书一样。
d
dwcz
这就是强迫症,文人通病。在象牙塔待长了,必患此症。原作者不过是明白了一个道理:原来的自己不过是复读机,后来的自己才是自己。
优秀程序员眼中的整洁代码

有多少程序员,就有多少定义。所以我只询问了一些非常知名且经验丰富的程序员。 优秀程序员眼中的整洁代码 Bjarne Stroustrup,C++语言发明者,C++ Programming Language(中译版《C++程序设...

风铃无声江舟听雨
2017/11/09
0
0
优秀程序员眼中的整洁代码

有多少程序员,就有多少定义。所以我只询问了一些非常知名且经验丰富的程序员。 Bjarne Stroustrup,C++语言发明者,C++ Programming Language(中译版《C++程序设计语言》)一书作者。 我喜...

两味真火
2017/08/06
3.4K
15
彻底抛弃 jQuery ,不然还留着过年?

我以前很喜欢 jQuery,而且说实话,我是先学jQuery,再学 JavaScript 的。所以我写这篇文章有点像是在背叛 jQuery。 我知道,关于为什么不应该用 jQuery 的文章已经汗牛充栋,但我只是想说下...

KaysonLi
2019/11/19
0
0
[译] Async 函数,让promise更友好!

原文链接 另,断断续续翻译了好几天,在发表的时候去搜索了下有没人翻译了,因为这确实是篇好文章。还真有:文章链接,看了下,这篇翻译的专业些,大家可以去看看。 Async 函数是一个非常了不...

kraaas
2019/09/29
0
0
修炼,从这一秒开始

不毁灭过去的自己,就不会有新生。和过去的自己彻底说再见! 从这一秒开始行动,从灵魂深处执行。内化在每一秒的行动中,融入血液,从此蜕变。让自己拥有一个强大的灵魂! 男性魅力七维模型:...

zhangyujsj
2015/12/31
87
0

没有更多内容

加载失败,请刷新页面

加载更多

在两个日期之间查找对象MongoDB

我一直在围绕在mongodb中存储推文,每个对象看起来像这样: {"_id" : ObjectId("4c02c58de500fe1be1000005"),"contributors" : null,"text" : "Hello world","user" : { "following......

javail
23分钟前
35
0
《aelf经济和治理白皮书》重磅发布:为DAPP提供治理高效、价值驱动的生态环境

2020年2月17日,aelf正式发布《aelf经济和治理白皮书》,这是aelf继项目白皮书后,在aelf网络经济模型和治理模式方面的权威论述。 《aelf经济和治理白皮书》描述了aelf生态中各个角色及利益的...

AELF开发者社区
34分钟前
44
0
EditText的首字母大写

我正在开发一个小小的个人待办事项列表应用程序,到目前为止,一切都运行良好。 我想知道一个小怪癖。 每当我去添加一个新项目时,我都会看到一个带有EditText视图的Dialog。 当我选择EditT...

技术盛宴
38分钟前
30
0
战疫 | 高德工程师如何在3天上线“医护专车”

新冠状病毒肺炎疫情突袭,无数医护人员放弃与家人团聚,明知凶险,仍然奋战在一线。但因为武汉公交、地铁、网约车停运,医护人员上下班很难。白衣天使疾呼打车难。 (截图摘自《财经国家周刊...

amap_tech
45分钟前
41
0
img在IE中无法按比例显示

在IE浏览器中使用img标签当给img标签设置width:98%时,显示时还是会把img的原始高度显示出来 解决方式给父标签设置width,但width不能使用100%需要指定一个值 <div style="width:900px;"> ...

有理想的鸭子
46分钟前
45
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部