头图

🤫 小声提醒 🤫

关注 Zilliz 微信公众号并回复「clean code」

获取《代码整洁之道》超详细思维导图

图片

我们为什么要追求整洁的代码?

《代码整洁之道》(Clean Code)提出,代码质量与其整洁度成正比。

只是让代码跑起来是不够的,因为需求不断增长,代码需要与时俱进:增加新的特征、修改已有的功能,优化性能……大工程量的项目需要由团队共同维护代码。

在团队协作中,冗余的逻辑和混乱的代码会让浪费大量的时间和资源,毕竟谁都不想捏着鼻子维护一座「💩💩⛰️」。因此,优秀的软件工程师追求代码上的「洁癖」,以写出赏心悦目、可扩展、易维护的代码为己任。

图片

《代码整洁之道》这本书正是一本代码清洁指南。这本书的作者是软件行业泰斗 Robert Martin,他是设计模式和敏捷开发的先驱,被后辈程序员尊称为「Uncle Bob」。2001 年,他和另外十六位顶级软件行业领军人物共同签署了《敏捷开发宣言》,为高效、可协作的软件开发流程建立了核心价值观,而《代码整洁之道》帮助敏捷开发打下基础,提供了一套行之有效的操作指南。

无论你是开发人员、软件工程师,还是项目经理、团队领导,如果你希望保持专业、不断精进,都应该读一读这本书。

你将从这本书中获得:

  • 学会如何区分代码的好坏,快速识别「脏乱」代码
  • 学会优化代码,让代码正确、简洁、优美、可持续
  • 让代码易于阅读和理解,帮你的队友节约时间
  • 养成使用自动化测试和单元测试的习惯,避免恶性循环
  • 理解真正的专业精神

整洁的代码长啥样?

第一章中,作者引用了 C++ 语言发明者 Bjarne Stroustrup 的话:

代码逻辑应当直截了当,叫缺陷难以隐藏;尽量减少依赖关系,使之便于维护;依据某种分层战略完善错误处理代码;性能调至最优,省得麻烦别人做没规矩的优化,搞出一堆混乱来。整洁的代码只做好一件事。

除此之外,整洁的代码还应该:

  • 便于阅读,可由作者之外的开发者增补
  • 有单元测试和验收测试
  • 使用有意义的命名
  • 依赖关系尽量精简
  • 代码应通过其字面表达含义
  • 没有重复代码

如何「清理」你的代码?

作者以自己走过的弯路为例,给出大量「清理代码」的案例。通过一个个具体的案例,作者手把手带领读者将问题的代码库转化为一个健壮和高效的代码库。

让我们花一两分钟看一个案例,下面的代码,你能看懂多少?

代码清单 3-1 HtmlUtil.java (坏例子)
public static String testableHtml(
  PageData pageData,
  boolean includeSuiteSetup
) throws Exception {
  WikiPage wikiPage = pageData.getWikiPage();
  StringBuffer buffer = new StringBuffer();
  if (pageData.hasAttribute("Test")) {
    if (includeSuiteSetup) {
      WikiPage suiteSetup =
      PageCrawlerImpl.getInheritedPage(
              SuiteResponder.SUITE_SETUP_NAME, wikiPage
      );
      if (suiteSetup != null) {
      WikiPagePath pagePath =
        suiteSetup.getPageCrawler().getFullPath(suiteSetup);
      String pagePathName = PathParser.render(pagePath);
      buffer.append("!include -setup .")
            .append(pagePathName)
            .append("\n");
      }
    }
    WikiPage setup =
      PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
    if (setup != null) {
      WikiPagePath setupPath =
        wikiPage.getPageCrawler().getFullPath(setup);
      String setupPathName = PathParser.render(setupPath);
      buffer.append("!include -setup .")
            .append(setupPathName)
            .append("\n");
    }
  }
  buffer.append(pageData.getContent());
  if (pageData.hasAttribute("Test")) {
    WikiPage teardown =
      PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
    if (teardown != null) {
      WikiPagePath tearDownPath =
        wikiPage.getPageCrawler().getFullPath(teardown);
      String tearDownPathName = PathParser.render(tearDownPath);
      buffer.append("\n")
            .append("!include -teardown .")
            .append(tearDownPathName)
            .append("\n");
    }
    if (includeSuiteSetup) {
      WikiPage suiteTeardown =
        PageCrawlerImpl.getInheritedPage(
                SuiteResponder.SUITE_TEARDOWN_NAME,
                wikiPage
        );
      if (suiteTeardown != null) {
        WikiPagePath pagePath =
          suiteTeardown.getPageCrawler().getFullPath (suiteTeardown);
        String pagePathName = PathParser.render(pagePath);
        buffer.append("!include -teardown .")
              .append(pagePathName)
              .append("\n");
      }
    }
  }
  pageData.setContent(buffer.toString());
  return pageData.getHtml();
}

在上面的代码中,有太多不同层级的抽象、奇怪的字符串、函数调用,混以双重嵌套、用标识来控制的 if 语句等,令人头疼。只要做几个简单的方法抽离和重命名操作,加上一点点重构,就能在接下来的几行代码中搞定:

代码清单 3-2 HtmlUtil.java(重构之后)
public static String renderPageWithSetupsAndTeardowns(
  PageData pageData, boolean isSuite
) throws Exception {
  boolean isTestPage = pageData.hasAttribute("Test");
  if (isTestPage) {
    WikiPage testPage = pageData.getWikiPage();
    StringBuffer newPageContent = new StringBuffer();
    includeSetupPages(testPage, newPageContent, isSuite);
    newPageContent.append(pageData.getContent());
    includeTeardownPages(testPage, newPageContent, isSuite);
    pageData.setContent(newPageContent.toString());
  }

  return pageData.getHtml();
}

这段代码比第一个例子清晰多了,你大概能明白,这个函数要把一些设置和拆解页放入一个测试页面,再渲染为 HTML。

但这样优化还不够整洁,作者说,「函数的第一规则是要短小。第二条规则是还要更短小。」

再次重构后,代码终于一目了然:

代码清单 3-3 HtmlUtil.java(再次重构之后)
public static String renderPageWithSetupsAndTeardowns(
  PageData pageData, boolean isSuite
) throws Exception {
  if (isTestPage(pageData))
    includeSetupAndTeardownPages(pageData, isSuite);
  return pageData.getHtml();
}

需要说明的是,《代码整洁之道》中的案例都是用 Java 语言写的,有一部分案例专门针对 Java。如果你使用其他语言,那么可以移花接木,理解书中的核心思想即可。

小编在 GitHub 上找到了一个用 JavaScript 去诠释「Clean Code」理念的项目,该项目给出大量 good / bad 两面的范例,并辅以注释作为说明,帮助读者理解「Clean Code」的理念 👇👇👇

图片

Clean Code Javascript 代码地址:https://github.com/ryanmcderm...

气味和启发

如何「闻香识代码」?作者总结了一些「坏气味」和「好气味」。「坏气味」指的是代码中某些疏漏、某些小错误,开发人员可以通过这些细节上的征兆在代码中追捕到更大问题;「好气味」指的是一些应该遵守的规则。

以写注释为例,「坏气味」包括:

  • 冗余的注释
  • 被注释掉的代码(应该及时删除这些代码)
  • ……

对于命名而言,「好气味」的例子包括:

  • 采用描述性的名称
  • 名称无歧义
  • 使用标准命名方法
  • 说明副作用
  • ……

这份清单很难穷尽所有的规则,清单背后所体现的价值体系才是「代码整洁」的目标。

想要了解更多详细内容?关注 Zilliz 微信公众号并回复书名「clean code」,即可获得小编整理的《代码整洁之道》高清思维导图。

福利时间

你是如何提升代码水平的?

你最想吐槽哪些不好的代码习惯?

对技术感兴趣的你,有哪些心头好书?

欢迎在微信评论区留言你的想法

我们将选取一位小伙伴,

赠送纸质版《代码整洁之道》一本!

积累代码量很重要,
读书、读好书也很重要。
「Zilliz 好书推荐」栏目,
旨在与你分享技术成长相关的书籍,
与你一起先把书读厚,再把书读薄。

Zilliz 以重新定义数据科学为愿景,致力于打造一家全球领先的开源技术创新公司,并通过开源和云原生解决方案为企业解锁非结构化数据的隐藏价值。

Zilliz 构建了 Milvus 向量数据库,以加快下一代数据平台的发展。Milvus 数据库是 LF AI & Data 基金会的毕业项目,能够管理大量非结构化数据集,在新药发现、推荐系统、聊天机器人等方面具有广泛的应用。


Zilliz
154 声望829 粉丝

Vector database for Enterprise-grade AI