likes
comments
collection
share

再见,整洁代码

作者站长头像
站长
· 阅读数 78

原文链接:Goodbye, Clean Code

作者:dan abramov

那是一个深夜。

我的同事刚刚检查了他们写了一周的代码。我们正在开发一个图形编辑器画布,他们实现了通过拖动矩形和椭圆边缘的小手柄来调整其大小的功能。

这段代码是奏效的。

但代码是有重复的。每个形状(如矩形或椭圆形)都有一组不同的手柄,向不同方向拖动每个手柄会以不同的方式影响形状的位置和大小。如果用户按住 Shift 健,我们还需要在调整大小时保持比例。这需要大量的数学计算。

代码看起来是这样的:

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};
 
let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};
 
let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },  
}
 
let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 10 repetitive lines of math
  },
};

这种重复的数学计算真的困扰着我。

它一点都不整洁。

大多数重复是在相似的方向之间。例如, Oval.resizeLeft()Header.resizeLeft() 有相似之处。这是因为他们都处理了拖动左侧的手柄。

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

我因此有了一个主意。

我们可以通过像这样对代码进行分组,从而消除所有的重复::

let Directions = {
  top(...) {
    // 5 unique lines of math
  },
  left(...) {
    // 5 unique lines of math
  },
  bottom(...) {
    // 5 unique lines of math
  },
  right(...) {
    // 5 unique lines of math
  },
};
 
let Shapes = {
  Oval(...) {
    // 5 unique lines of math
  },
  Rectangle(...) {
    // 5 unique lines of math
  },
}

然后组成他们的行为:

let {top, bottom, left, right} = Directions;
 
function createHandle(directions) {
  // 20 lines of code
}
 
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 lines of code
}
 
let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

代码的总大小减少了一半,重复的代码也完全消失了!如此简洁。如果我们想改变某个特定方向或形状的行为,我们可以在一个地方完成,而不用到处更新方法。

夜已经深了(我走神了)。我把重构的结果报告给主控程序,然后上床睡觉,为自己解开了同事混乱的代码而感到自豪。

第二天早上

......结果不尽如人意。

我的老板邀请我进行了一次一对一的谈话,在谈话中,他们礼貌地要求我恢复我的变更。我大吃一惊。旧的代码一团糟,而我的代码干干净净!

我勉为其难地答应了,但过了好几年才发现他们是对的。

这是一个阶段

执着于 "简洁代码 "和删除重复代码是我们很多人都会经历的一个阶段。当我们对自己的代码不自信时,很容易将自我价值感和职业自豪感寄托在可以衡量的东西上。一套严格的检查规则、一个命名模式、一个文件结构、一个没有重复的代码。

你无法自动删除重复内容,但通过练习确实会变得更容易。通常每次修改后,你都能判断出重复是少了还是多了。因此,删除重复就像是在改善代码的某些客观指标。更糟糕的是,它扰乱了人们的认同感: "我是那种写整洁代码的人"。它的威力不亚于任何一种自欺欺人的行为。

一旦我们学会了如何创建 抽象,就很容易被这种能力所吸引,只要看到重复的代码,就会凭空抽象出来。经过几年的编码工作,我们发现重复的代码随处可见,而抽象就是我们新的超级能力。如果有人告诉我们抽象是一种 美德,我们就会接受它。而且,我们还会开始指责其他人不崇尚 "简洁"。

现在我明白了,我的 "重构 "在两个方面都是一场灾难:

  • 首先,我没有和写代码的人谈过。我重写了代码,并在没有他们意见的情况下进行了检查。即使这是一种改进(我不再相信这一点了),这也是一种糟糕的方式。一个健康的工程团队需要不断建立信任。不经讨论就改写队友的代码,会极大地打击你们在代码库上有效合作的能力。
  • 其次,没有什么是免费的。我的代码用改变需求的能力来换取减少重复,这不是一个好的交易。例如,我们后来需要为不同形状的不同手柄设置许多特殊情况和行为。要做到这一点,我的抽象必须变得更加复杂几倍,而在最初的 "混乱 "版本中,这种变化仍然很容易。

我是说你应该写“脏”(dirty)代码吗?不。我建议深入思考你说“整洁”或“肮脏”的意思。你会有一种反抗的感觉吗?正义感?美?优雅?您有多大把握能说出与这些良好品质相对应的具体工程成果?它们究竟如何影响代码的编写方式和 修改方式?

我肯定没有深入思考这些问题。我考虑了很多关于代码的样子 ,但没有考虑它如何与一群软弱的人类一起进化的。

编码是一段旅程。想想你从第一行代码到现在已经走了多远。第一次看到函数或重构一个类如何使复杂的代码变得简单是一件令人愉快的事情。如果你对自己的技术感到自豪,那么追求代码的简洁性是很有诱惑力的。先这样做尝试一段时间吧。

但不要止步于此。不要做一个整洁的代码狂热分子。整洁的代码不是目标。它只是试图从我们所处理的系统的巨大复杂性中变得更有意义。这是一种防御机制:当您还不确定一项代码变更将如何影响代码库时,但你需要在一片未知的海洋中得到指引,就可以使用它。

让整洁的代码指导你。 然后随它去吧。

转载自:https://juejin.cn/post/7367195057558175756
评论
请登录