回想起来,我觉得我们似乎误读了UncleBob的CleanCode,至少我们错误地将所谓的Clean等同于可读代码。特别不幸的是,在CleanCode一书中,第2章到第5章都是关于可读代码的,这强化了这种错误印象。很多有代码洁癖的程序员把代码可读性视为至高无上的真理,将其视为高质量代码最重要的特征,封为“神坛”。众所周知,Bob大叔在CleanCode第一章中通过别人的口为所谓的“CleanCode”正名:所谓的cleancode不仅仅是“清晰”。根据KentBeck的简单设计规则,第一件事实际上不是可读性,而是“通过所有测试”。隐藏的意思是满足用户正确的需求,因为测试可以看成是用户提出的需求。这个要求不仅是针对业务,还有质量属性,比如性能、安全等属性。消除重复和提高表达能力这两点有时会相互促进,去除冗余代码会让代码更清晰;但是,有时它们会相互冲突,消除重复的成本可能会比较高,导致提取出太多微小的实体,从而增加阅读障碍。所以,我经常把Bob大叔的“函数第一法则是要短,第二法则还要短”。作为一种矫枉过正的强迫症。对于喜欢写大函数的程序员来说,确实有必要牢记这个原则,但切记不要把它当作格言。保证函数短是有前提的。仔细阅读KentBeck的简单设计原则,按重要性排序:可以通过所有测试;不要重复代码;体现设计者的意图;如果没有必要,请不要添加实体(方法、函数、类)等待)。如果程序满足客户的需求,没有重复的代码,功能的表达也足够清晰,足以体现设计者的意图,为什么还要继续把功能抽取出来,把功能做得极短?真正有意义的原则是“让函数只做一件事”。因此,CleanCode书中Bob大叔展示的FitNesse中HtmlUtil.java的第二次重构是没有必要的。第一次重构后,代码如下所示:(testPage,newPageContent,isSuite);newPageContent.append(pageData.getContent());includeTeardownPages(testPage,newPageContent,isSuite);pageData.setContent(newPageContent.toString());}returnpageData.getHtml();}这段代码的结构和层次非常清晰,实现细节也得到了充分的封装和隐藏。如果要说缺点的话,或许可以把下面的代码再抽出来,使其符合SLAP原则(单一抽象层次原则):newPageContent.append(pageData.getContent());//Extractas:includeTestContents(testPage,newPageContent)和Bob大叔做的第二次重构,除了让方法更短、隐藏太多细节、引入更多层次之外,到底为代码的清晰度带来了什么?publicstaticStringrenderPageWithSetupsAndTeardowns(PageDatapageData,booleanisSuite)throwsException{if(isTestPage(pageData))includeSetupAndTeardownPages(pageData,isSuite);returnpageData.getHtml();}太多了!有时,为了去除重复,需要从相似的代码中找到一种模式或某种抽象,并将其提取出来。过多的抽取会使代码难以阅读,因为抽取的手段往往会引入“间接”。正如MartinFowler所说:“间接可能会有帮助,但不必要的间接总是让人不舒服”。不必要的间接访问通常会妨碍代码的直接和清晰。如果去除重复的唯一好处是避免类中的一点私有重复,那么去除这种重复确实没有多大意义。我喜欢干净的代码,但我认为保持代码正确、健壮和高效同样重要。因为代码整洁,我曾经把大量的非空判断、非法检查、异常处理视为干扰代码清晰的祸害,但如果不做这些“脏活”,代码可能会变得不健壮。在Java中,如果真的想避免这些判断,可以考虑转移责任。通过定义CheckedException,将异常处理的责任转移给方法的调用者。但是,盲目推卸责任总是不负责任的。实现每个方法和每个类的程序员应该确保他们的代码是自治的。以下代码:@Overridepublicvoidrun(){if(isFromFile){if(hasQuery){thrownewRuntimeException("both--executeand--filespecified");}try{query=Files.toString(newFile(clientOptions.file),UTF_8);hasQuery=true;}catch(IOExceptione){thrownewRuntimeException(format("Errorreadingfromfile%s:%s",clientOptions.file,e.getMessage()));}}}这样的代码确实不够优雅,但是足够了判断保证了代码的正确性和健壮性。我只能说,在满足这两点的前提下,可以巧妙地使用防御性编程和Optional之类的东西,避免冗余的嵌套或者分支,从而提高代码的可读性。EffectiveJava总结了高质量代码的几个特征:清晰、正确、可用、健壮、灵活和可维护。我觉得这个总结很中肯。写代码的时候不要太偏执。我认为不管任何场景都一味追求代码的可读性(清晰度),一味地重申DRY是不负责任的。也许是因为我老了,不再理想主义了;但更多的是因为我看到太多追求所谓“干净代码”的程序,不想考虑复杂繁琐的异常,导致程序失败。不坚固;因为去重带来的不必要,间接影响了代码的简洁和整洁,甚至影响了代码运行的性能。干净的代码是必须的,但它不是衡量代码质量的唯一标准!【本文为专栏作家“张艺”原创稿件,转载请联系原作者】点此阅读作者更多好文
