发布于 2026-01-06 2 阅读
0

编写整洁代码练习 - 第一部分

编写整洁代码练习 - 第一部分

插图由Icons8 的Ivan Haidutski绘制

你可能已经阅读和收听过很多关于代码整洁之道的文章,也可能对YAGNI、DRY和KISS之类的缩写感到厌倦。所有这些通常都会被你直接扔进垃圾桶,因为你没有进行足够的实践。

多年来,我一直在代码审查中阅读其他人的代码,练就了一双能发现糟糕代码的“眼睛”,我认为通过阅读我设计的场景,你也可以培养这种能力。

以下示例代码未必有缺陷,我的意思是,假设它们没有任何 bug,也能正常运行,但它们的可维护性还不够好。请阅读每个示例,尝试找出问题所在,并设想你会如何解决它。

情景1

function canBuyBeer(age, money) {
  if(age >= 21 && money >= 20) {
    return true
  }
  return false
}
Enter fullscreen mode Exit fullscreen mode

出了什么问题?

(请先完成以上代码后再阅读)

这个例子的问题在于 if 语句中使用了任意数字。根据方法上下文,你可能会推断出21是法定饮酒年龄,20是啤酒价格,但乍一看并非如此简单。这些通常被称为magic numbers

解决方案

解决这个问题的一个方法是为这些数字创建命名常量。这样可以使代码更易于阅读。

function canBuyBeer(age, money) {
  const legalDrinkingAge = 21
  const beerPrice = 20
  if(age >= legalDrinkingAge && money >= beerPrice) {
    return true
  }
  return false
}
Enter fullscreen mode Exit fullscreen mode

此外,如果将来某些事情发生变化,例如啤酒价格,通过更改常量值而不是查找和替换出现的位置,出错的可能性会更小20

情景二

function shouldShowImage(itemIndex, article, showAllImages) {
  return [0, 1, 2].includes(itemIndex)
    ? !!article.imageUrl
    : showAllImages
      ? !!article.imageUrl
      :false
}
Enter fullscreen mode Exit fullscreen mode

出了什么问题?

(记住,先尝试自己找出问题所在)
这个 return 语句里发生的事情太多了。写这段代码的人可能很聪明,他运用惯用手法,用一行或几行代码就解决了问题,这就是为什么它被称为clever code……

解决方案

只需明确说明预期行为,并使其易于阅读,即使这意味着将代码拆分成更多行。

function shouldShowImage(itemIndex, article, showAllImages) {
  if(!article.imageUrl) {
    return false
  }
  if(showAllImages) {
    return true
  }
  const isItemOneTwoOrThree = [0,1,2].includes(itemIndex)
  if(isItemOneTwoOrThree) {
    return true
  }

  return false
}
Enter fullscreen mode Exit fullscreen mode

示例和解决方案之间有很多重构步骤,但我向你保证,两者的行为是相同的。

如果同行辩称他们编写的巧妙代码会使应用程序变得更大,那很可能不是真的,大多数现代语言在编译或压缩后都会比人类编写的任何巧妙代码都要小。

情景 3

function getArea(shape, width, height, radius) {
  if(shape === 'circle'){
    return Math.PI * radius * radius
  } else if(shape === 'square') {
    return width * width
  } else if(shape === 'rectangle') {
    return width * height
  }
}
Enter fullscreen mode Exit fullscreen mode

出了什么问题?

这种方法承担的责任太多了。每当有人添加新形状时,都if/else需要创建一个新的语句。此外,虽然目前的面积计算只需一行代码,但如果计算变得更加复杂,例如需要验证输入,那么这种方法就会变得非常庞大。

解决方案

将它们分成不同的策略,并采用不同的方法,这样,即使它们规模扩大,也更容易维护。

const circleStrategy = (shape) => Math.PI * shape.radius * shape.radius

const squareStrategy = (shape) => shape.width * shape.width

const rectangleStrategy = (shape) => shape.width * shape.height

const areaStrategies = {
  circle: circleStrategy,
  square: squareStrategy,
  rectangle: rectangleStrategy
}

function getArea (shapeName, width, height, radius) {
  const shapeObject = { width, height, radius }
  const strategy = areaStrategies[shapeName]
  return strategy(shapeObject)
}
Enter fullscreen mode Exit fullscreen mode

请注意,我们还改进了选择策略的机制,使用字典而不是多个if/else语句。

好了,今天的练习就到这里,希望它能帮助你在下次代码审查时更有敏锐的洞察力。

我会在以后的文章中提供更多例子,如果您有更多场景的想法,请在评论区留言。

您也可以点击此处查看本系列的第二部分

文章来源:https://dev.to/ederchrono/clean-code-exercises-part-1-5gl7