【译】和整洁代码说再见

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

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

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

如下是译文。编辑器

那是一个深夜。ide

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

代码工做良好。可是有重复代码。每一个形状(例如矩形和椭圆)都有一组不一样的手柄,在不一样的方向上拖动每一个手柄,会以不一样的改变形状的位置和大小。若是用户按住 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() 的代码相似。由于它们都要处理左边的拖拽操做。code

另外,同一形状的不一样方法也有类似之处。例如,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

相关文章
相关标签/搜索