查看原文
其他

如何写出让同事膜拜的漂亮代码?

点击上方“Python大本营”,选择“置顶公众号”

python大本营  IT人的职业提升平台

“代码千万行,注释第一行;编程不规范,同事两行泪”;"道路千万条,安全第一条。代码不规范,亲人两行泪。"在技术圈广为盛传,可见代码不规范让程序员们是多么的头痛。

如何让你的代码整洁而优雅?如何写出让同事膜拜的漂亮代码?


01何谓重构


重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构与性能优化有很多相似之处:两者都需要修改代码,并且两者都不会改变程序的整体功能。两者的差别在于其目的:重构是为了让代码“更容易理解,更易于修改”。这可能使程序运行得更快,也可能使程序运行得更慢。在性能优化时,我只关心让程序运行得更快,最终得到的代码有可能更难理解和维护,对此我有心理准备。

02两顶帽子


Kent Beck提出了“两顶帽子”的比喻。使用重构技术开发软件时,我把自己的时间分配给两种截然不同的行为:添加新功能和重构。
添加新功能时,我不应该修改既有代码,只管添加新功能。通过添加测试并让测试正常运行,我可以衡量自己的工作进度。
重构时我就不能再添加功能,只管调整代码的结构。此时我不应该添加任何测试(除非发现有先前遗漏的东西),只在绝对必要(用以处理接口变化)时才修改测试。

03为何重构

我不想把重构说成是包治百病的万灵丹,它绝对不是所谓的“银弹”。不过它的确很有价值,尽管它不是一颗“银弹”,却可以算是一把“银钳子”,可以帮你始终良好地控制自己的代码。重构是一个工具,它可以(并且应该)用于以下几个目的。
  • 重构改进软件的设计

  • 重构使软件更容易理解

  • 重构帮助找到bug

  • 重构提高编程速度



04何时重构


在我编程的每个小时,我都会做重构。有几种方式可以把重构融入我的工作过程里。
  • 预备性重构:让添加新功能更容易

  • 帮助理解的重构:使代码更易懂

  • 捡垃圾式重构

  • 有计划的重构和见机行事的重构

  • 长期重构

  • 复审代码时重构

  • 何时不应该重构



05重构的挑战


每当有人大力推荐一种技术、工具或者架构时,我总是会观察这东西会遇到哪些挑战,毕竟生活中很少有晴空万里的好事。你需要了解一件事背后的权衡取舍,才能决定何时何地应用它。
我认为重构是一种很有价值的技术,大多数团队都应该更多地重构,但它也不是完全没有挑战的。有必要充分了解重构会遇到的挑战,这样才能做出有效应对。

06重构,第一个示例

在本书第1版中,我使用的示例程序是为影片出租店的顾客打印一张详单。放到今天,很多人可能要问了:“影片出租店是什么?”为了避免过多回答这个问题,我翻新了一下示例,将其包装成一个仍有古典韵味又尚未消亡的现代示例。
设想有一个戏剧演出团,演员们经常要去各种场合表演戏剧。通常客户(customer)会指定几出剧目,而剧团则根据观众(audience)人数及剧目类型来向客户收费。该团目前出演两种戏剧:悲剧(tragedy)和喜剧(comedy)。给客户发出账单时,剧团还会根据到场观众的数量给出“观众量积分”(volume credit)优惠,下次客户再请剧团表演时可以使用积分获得折扣—你可以把它看作一种提升客户忠诚度的方式。
该剧团将剧目的数据存储在一个简单的JSON文件中。
plays.json…
1{
2  "hamlet": {"name""Hamlet""type""tragedy"},
3  "as-like": {"name""As You Like It""type""comedy"},
4  "othello": {"name""Othello""type""tragedy"}
5}
他们开出的账单也存储在一个JSON文件里。
invoices.json…
1[
2  {
3    "customer""BigCo",
4    "performances": [
5      {
6        "playID""hamlet",
7        "audience"55
8      },
9      {
10        "playID""as-like",
11        "audience"35
12      },
13      {
14        "playID""othello",
15        "audience"40
16      }
17    ]
18  }
19]
下面这个简单的函数用于打印账单详情。
1function statement (invoice, plays) {
2  let totalAmount = 0;
3  let volumeCredits = 0;
4  let result = `Statement for ${invoice.customer}\n`;
5  const format = new Intl.NumberFormat("en-US",
6                        { style: "currency", currency: "USD",
7                          minimumFractionDigits: 2 }).format;
8  for (let perf of invoice.performances) { 
9    const play = plays[perf.playID];
10    let thisAmount = 0;
11    switch (play.type) {
12    case "tragedy":
13      thisAmount = 40000;
14      if (perf.audience > 30) {
15        thisAmount += 1000 * (perf.audience - 30);
16      }
17      break;
18  case "comedy":
19      thisAmount = 30000;
20      if (perf.audience > 20) {
21        thisAmount += 10000 + 500 * (perf.audience - 20);
22      }
23      thisAmount += 300 * perf.audience;
24      break;
25    default:
26        throw new Error(`unknown type${play.type}`);
27    }
28    // add volume credits
29    volumeCredits += Math.max(perf.audience - 30, 0);
30    // add extra credit for every ten comedy attendees
31    if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
32    // print line for this order
33    result += ` ${play.name}${format(thisAmount/100)} (${perf.audience} seats)\n`;
34    totalAmount += thisAmount;
35  }
36  result += `Amount owed is ${format(totalAmount/100)}\n`;
37  result += `You earned ${volumeCredits} credits\n`;
38  return result;
39}
用上面的数据文件(invoices.jsonplays.json)作为测试输入,运行这段代码,会得到如下输出:
1Statement for BigCo
2  Hamlet: $650.00 (55 seats)
3  As You Like It: $580.00 (35 seats)
4  Othello: $500.00 (40 seats)
5Amount owed is $1,730.00
6You earned 47 credits

你还有什么技巧,一起来分享一下!

【End】

—— 推 荐 阅 读 ——你点的每个“在看”,我都认真当成了喜欢

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存