回想起来,我觉得我们似乎在误读Uncle Bob的Clean Code,至少我们错误地将所谓Clean与可读性代码简单地划上了等号。尤为不幸的是,在Clean Code一书中,从第二章到第五章都围绕着可读性代码做文章,于是加深了这种错误的印象。
许多具有代码洁癖的程序员将代码可读性视为神圣不可侵犯的真理,并奉其为高质量代码的最重要特征,封上了“神坛”。殊不知,Uncle Bob在Clean Code的第一章就通过别人之口对所谓“Clean Code”进行了正名:所谓整洁代码并非仅仅是“清晰”这么简单。
按照Kent Beck的简单设计规则,排在第一位的其实不是可读性,而是“通过所有测试”。其中潜藏的含义是满足用户正确的需求,因为测试可以看做是用户提出的需求。这个需求不仅仅是业务上的,还包括质量属性的需求,例如性能、安全等属性。
消除重复和提高表达力这两点,有时候会互相促进,去除了冗余的代码,会让代码变得更加清晰;然而,有时候却又互相冲突,消除重复的成本可能会比较高,导致提取了太多细碎微小的实体,反而增加了阅读障碍。
故而我常常将Uncle Bob提出的“函数的第一规则是要短小。第二条规则是还要更短小。”看做是一种矫枉过正的强迫。对于那种喜欢编写大函数的程序员而言,确实需要时刻铭记这一原则,但切记不要将其视为最高准则。保证函数短小是有前提的,仔细阅读Kent Beck的简单设计原则,依其重要顺序:
能通过所有测试;
没有重复代码;
体现设计者的意图;
若无必要,勿增实体(方法、函数、类等)。
如果程序满足了客户需求,没有重复代码,函数的表达已经足够清晰地体现设计者意图,为何还要不断地提取函数,使得函数变得极为短小呢?真正有意义的原则是“让函数只做一件事情”。
正因为此,在Clean Code书中,Uncle Bob展示的对FitNesse中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(); }