新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 牛人業(yè)話 > 【《代碼整潔之道》精讀與演繹】之三 整潔代碼的函數(shù)書(shū)寫(xiě)準(zhǔn)則

【《代碼整潔之道》精讀與演繹】之三 整潔代碼的函數(shù)書(shū)寫(xiě)準(zhǔn)則

作者: 時(shí)間:2016-08-31 來(lái)源:網(wǎng)絡(luò) 收藏

  一、引言

本文引用地址:http://2s4d.com/article/201608/296317.htm

  以下引言的內(nèi)容,有必要伴隨這個(gè)系列的每一次更新,這次也不例外。

  《整潔之道》這本書(shū)提出了一個(gè)觀點(diǎn):質(zhì)量與其整潔度成正比,干凈的,既在質(zhì)量上可靠,也為后期維護(hù)、升級(jí)奠定了良好基礎(chǔ)。書(shū)中介紹的規(guī)則均來(lái)自作者多年的實(shí)踐經(jīng)驗(yàn),涵蓋從命名到重構(gòu)的多個(gè)編程方面,雖為一“家”之言,然誠(chéng)有可資借鑒的價(jià)值。

  但我們知道,很多時(shí)候,理想很豐滿,現(xiàn)實(shí)很骨感,也知道人在江湖,身不由己。因?yàn)轫?xiàng)目的緊迫性,需求的多樣性,我們無(wú)法時(shí)時(shí)刻刻都寫(xiě)出整潔的代碼,保持自己輸出的都是高質(zhì)量、優(yōu)雅的代碼。

  但若我們理解了代碼整潔之道的精髓,我們會(huì)知道怎樣讓自己的代碼更加優(yōu)雅、整潔、易讀、易擴(kuò)展,知道真正整潔的代碼應(yīng)該是怎么樣的,也許就會(huì)漸漸養(yǎng)成持續(xù)輸出整潔代碼的習(xí)慣。

  而且或許你會(huì)發(fā)現(xiàn),若你一直保持輸出整潔代碼的習(xí)慣,長(zhǎng)期來(lái)看,會(huì)讓你的整體效率和代碼質(zhì)量大大提升。

  二、本文涉及知識(shí)點(diǎn)思維導(dǎo)圖

  國(guó)際慣例,先放出這篇文章所涉及內(nèi)容知識(shí)點(diǎn)的一張思維導(dǎo)圖,就開(kāi)始正文。大家若是疲于閱讀文章正文,直接看這張圖,也是可以Get到本文的主要知識(shí)點(diǎn)的大概。

  

 

  三、整潔代碼的書(shū)寫(xiě)準(zhǔn)則

  1 短小

  的第一規(guī)則是要短小。第二規(guī)則還是要短小。

  《代碼整潔之道》一書(shū)作者Bob大叔寫(xiě)道,“近40年來(lái),我寫(xiě)過(guò)各種長(zhǎng)度不同的。我寫(xiě)過(guò)令人憎惡的長(zhǎng)達(dá)3000行的厭物,也寫(xiě)過(guò)許多100行到300行的函數(shù),還寫(xiě)過(guò)20行到30行的。經(jīng)過(guò)漫長(zhǎng)的試錯(cuò),經(jīng)驗(yàn)告訴我,函數(shù)就該短小”。

  那么函數(shù)應(yīng)該有多短小合適呢?通常來(lái)說(shuō),應(yīng)該短于如下這個(gè)函數(shù):

  [cpp] view plain copy print?

  public static StringrenderPageWithSetupsAndTeardowns

  (PageData pageData, boolean isSuite

  )throws Exception

  {

  booleanisTestPage = pageData.hasAttribute("Test");

  if(isTestPage) {

  WikiPagetestPage = pageData.getWikiPage( );

  StringBuffernewPageContent = new StringBuffer( );

  includeSetupPages(testPage,newPageContent, isSuite);

  newPageContent.append(pageData.getContent());

  includeTeardownPages(testPage,newPageContent, isSuite);

  pageData.setContent(newPageContent.toString());

  }

  returnpageData.getHtml( );

  }

  而其實(shí),最好應(yīng)該縮短成如下的樣子:

  [csharp] view plain copy print?

  public static StringrenderPageWithSetupsAndTeardowns(

  PageDatapageData, boolean isSuite) throws Exception

  {

  if(isTestPage(pageData))

  includeSetupAndTeardownPages(pageData,isSuite);

  returnpageData.getHtml( );

  }

  總之,十行以內(nèi)是整潔的函數(shù)比較合適的長(zhǎng)度,若沒(méi)有特殊情況,我們最好將單個(gè)函數(shù)控制在十行以內(nèi)。

  評(píng)論區(qū)有一些討論,也放到正文來(lái)吧。

  “函數(shù)是否應(yīng)該足夠短小,算是《代碼整潔之道》中最具爭(zhēng)議的議題之一。

  書(shū)寫(xiě)短小函數(shù)的時(shí)候,其實(shí)我們不要忽略一點(diǎn),那就是,函數(shù)名名稱本身就具描述性。短小的函數(shù)構(gòu)成,如果要追根溯源了解內(nèi)部實(shí)現(xiàn),自然需要一層層找到最終的實(shí)現(xiàn)。但若是想大致知道這個(gè)函數(shù)到底做了什么,結(jié)合這個(gè)短小函數(shù)體內(nèi)具描述性的一些函數(shù)名,應(yīng)該也就一目了然了。試想,當(dāng)你眼前的這個(gè)函數(shù)是幾十上百上千行的龐然大物的時(shí)候,你能做到一眼就一目了然,將其大概做了什么了然于心嗎?函數(shù)短小的一方面優(yōu)點(diǎn),在這里就體現(xiàn)出來(lái)了。

  函數(shù)應(yīng)該短小這個(gè)議題,仁者見(jiàn)仁智者見(jiàn)智,在實(shí)際編碼過(guò)程中任何人都很難做到嚴(yán)格遵守,但大的方向,若想寫(xiě)出整潔的代碼,應(yīng)該去向短小的函數(shù)靠攏,對(duì)吧?”

  2 單一職責(zé)

  函數(shù)應(yīng)該只做一件事情。只做一件事,做好這件事。

  設(shè)計(jì)模式中有單一職責(zé)原則,我們可以把這條原則理解為代碼整潔之道中的函數(shù)單一職責(zé)原則。

  要判斷函數(shù)是不是只做了一件事情,還有一個(gè)方法,就是看能否再拆出一個(gè)函數(shù),該函數(shù)不僅只是單純地重新詮釋其實(shí)現(xiàn)。

  3 命名合適且具描述性

  “如果每個(gè)例程都讓你感到深合己意,那就是整潔的代碼。”要遵循這一原則,大半工作都在于為只做一件事的小函數(shù)取個(gè)好名字。函數(shù)越短小,功能越集中,就越便于取個(gè)好名字。

  別害怕長(zhǎng)名稱。長(zhǎng)而具有描述性的名稱,比短而令人費(fèi)解的名稱好。而且長(zhǎng)而具有描述性的名稱,比描述性的長(zhǎng)注釋要好。且選擇描述性的名稱能理清你關(guān)于模塊的設(shè)計(jì)思路,并幫你改進(jìn)之。當(dāng)然,如果短的名稱已經(jīng)足夠說(shuō)明問(wèn)題,還是越短越好。

  命名方式要保持一致。使用與模塊名一脈相承的短語(yǔ)、名詞和動(dòng)詞給函數(shù)命名。比如,includeSetupAndTeardownPages,includeSetupPages, includeSuiteSetupPage, and includeSetupPage等。這些名詞使用了類似的措辭,依序講述一個(gè)故事,就是是比較推崇的命名方式了。

  4 參數(shù)盡可能少

  最理想的函數(shù)參數(shù)形態(tài)是零參數(shù),其次是單參數(shù),再次是雙參數(shù),應(yīng)盡量避免三參數(shù)及以上參數(shù)的函數(shù),有足夠的理由才能用三個(gè)以上參數(shù)(多參數(shù)函數(shù))。

  函數(shù)參數(shù)中出現(xiàn)標(biāo)識(shí)符參數(shù)是非常不推崇的做法。有標(biāo)識(shí)符參數(shù)的函數(shù),很有可能不止在做一件事,標(biāo)示如果標(biāo)識(shí)符為true將這樣做,標(biāo)識(shí)符為false將那樣做。正確的做法應(yīng)該將有標(biāo)識(shí)符參數(shù)的函數(shù)一分為二,對(duì)標(biāo)識(shí)符為true和false分別開(kāi)一個(gè)函數(shù)來(lái)處理。

  5 避免重復(fù)

  重復(fù)的代碼會(huì)導(dǎo)致模塊的臃腫,整個(gè)模塊的可讀性可能會(huì)應(yīng)該重復(fù)的消除而得到提升。

  其實(shí)可以這樣說(shuō),重復(fù)可能是軟件中一切邪惡的根源,許多原則與實(shí)踐規(guī)則都是為控制與消除重復(fù)而創(chuàng)建的。仔細(xì)想一想,面向?qū)ο缶幊淌侨绾螌⒋a集中到基類,從而避免了冗余的。而面向方面編程(Aspect Oriented Programming)、面向組件編程(ComponentOriented Programming)多少也是消除重復(fù)的一種策略。這樣看來(lái),自子程序發(fā)明以來(lái),軟件開(kāi)發(fā)領(lǐng)域的所有創(chuàng)新都是在不斷嘗試從源代碼中消滅重復(fù)。

  重復(fù)而啰嗦的代碼,乃萬(wàn)惡之源,我們要盡力避免。

  四、范例

  有必要貼出一段書(shū)中推崇的整潔代碼作為本次函數(shù)書(shū)寫(xiě)準(zhǔn)則的范例。

  [csharp] view plain copy print?

  using System;

  public class SetupTeardownIncluder

  {

  private PageData pageData;

  private boolean isSuite;

  private WikiPage testPage;

  private StringBuffer newPageContent;

  private PageCrawler pageCrawler;

  public static String render(PageData pageData) throws Exception

  {

  return render(pageData, false);

  }

  public static String render(PageData pageData, boolean isSuite)throws Exception

  {

  return new SetupTeardownIncluder(pageData).render(isSuite);

  }

  private SetupTeardownIncluder(PageData pageData)

  {

  this.pageData = pageData;

  testPage = pageData.getWikiPage();

  pageCrawler = testPage.getPageCrawler();

  newPageContent = new StringBuffer();

  }

  private String render(boolean isSuite) throws Exception

  {

  this.isSuite = isSuite;

  if (isTestPage())

  includeSetupAndTeardownPages();

  return pageData.getHtml();

  }

  private boolean isTestPage() throws Exception

  {

  return pageData.hasAttribute("Test");

  }

  private void includeSetupAndTeardownPages() throws Exception

  {

  includeSetupPages();

  includePageContent();

  includeTeardownPages();

  updatePageContent();

  }

  private void includeSetupPages() throws Exception

  {

  if (isSuite)

  includeSuiteSetupPage();

  includeSetupPage();

  }

  private void includeSuiteSetupPage() throws Exception

  {

  include(SuiteResponder.SUITE_SETUP_NAME, "-setup");

  }

  private void includeSetupPage() throws Exception

  {

  include("SetUp", "-setup");

  }

  private void includePageContent() throws Exception

  {

  newPageContent.append(pageData.getContent());

  }

  private void includeTeardownPages() throws Exception

  {

  includeTeardownPage();

  if (isSuite)

  includeSuiteTeardownPage();

  }

  private void includeTeardownPage() throws Exception

  {

  include("TearDown", "-teardown");

  }

  private void includeSuiteTeardownPage() throws Exception

  {

  include(SuiteResponder.SUITE_TEARDOWN_NAME, "-teardown");

  }

  private void updatePageContent() throws Exception

  {

  pageData.setContent(newPageContent.toString());

  }

  private void include(String pageName, String arg) throws Exception

  {

  WikiPage inheritedPage = findInheritedPage(pageName);

  if (inheritedPage != null)

  {

  String pagePathName = getPathNameForPage(inheritedPage);

  buildIncludeDirective(pagePathName, arg);

  }

  }

  private WikiPage findInheritedPage(String pageName) throws Exception

  {

  return PageCrawlerImpl.getInheritedPage(pageName, testPage);

  }

  private String getPathNameForPage(WikiPage page) throws Exception

  {

  WikiPagePath pagePath = pageCrawler.getFullPath(page);

  return PathParser.render(pagePath);

  }

  private void buildIncludeDirective(String pagePathName, String arg)

  {

  newPageContent

  .append("n!include ")

  .append(arg)

  .append(" .")

  .append(pagePathName)

  .append("n");

  }

  }

  上面這段代碼,滿足了函數(shù)書(shū)寫(xiě)短小、單一職責(zé)、命名合適、參數(shù)盡可能少、不重復(fù)啰嗦這幾條準(zhǔn)則。整潔的函數(shù)代碼大致如此。

  五、小結(jié)

  大師級(jí)程序員把系統(tǒng)當(dāng)作故事來(lái)講,而不是當(dāng)做程序來(lái)寫(xiě)。這是之前已經(jīng)提到過(guò)的一個(gè)觀點(diǎn)。

  本文講述了如何編寫(xiě)良好函數(shù)的一些準(zhǔn)則,如果你遵循這些準(zhǔn)則,函數(shù)就會(huì)短小,有個(gè)好名字,而且被很好的歸置。不過(guò)永遠(yuǎn)不要忘記,我們真正的目標(biāo)在于講述系統(tǒng)的故事,而你編寫(xiě)的函數(shù)必須干凈利落的拼裝到一起,形成一種精確而清晰的語(yǔ)言,幫助你講故事。

  程序員,其實(shí)是故事家。

  六、本文涉及知識(shí)點(diǎn)提煉整理

  整潔代碼的函數(shù)書(shū)寫(xiě),可以遵從如下幾個(gè)原則:

  第一原則:短小。若沒(méi)有特殊情況,最好將單個(gè)函數(shù)控制在十行以內(nèi)。

  第二原則:?jiǎn)我宦氊?zé)。函數(shù)應(yīng)該只做一件事情。只做一件事,做好這件事。

  第三原則:命名合適且具描述性。長(zhǎng)而具有描述性的名稱,比短而令人費(fèi)解的名稱好。當(dāng)然,如果短的名稱已經(jīng)足夠說(shuō)明問(wèn)題,還是越短越好。

  第四原則:參數(shù)盡可能少。最理想的函數(shù)參數(shù)形態(tài)是零參數(shù),其次是單參數(shù),再次是雙參數(shù),應(yīng)盡量避免三參數(shù)及以上參數(shù)的函數(shù),有足夠的理由才能用三個(gè)以上參數(shù)。

  第五原則:盡力避免重復(fù)。重復(fù)的代碼會(huì)導(dǎo)致模塊的臃腫,整個(gè)模塊的可讀性可能會(huì)應(yīng)該重復(fù)的消除而得到提升。

  本文就此結(jié)束。

  下篇文章,我們將繼續(xù)《代碼整潔之道》的精讀與演繹,探討更多的內(nèi)容。

  Best Wish~



關(guān)鍵詞: 代碼 函數(shù)

評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉