短小
- 20世紀(jì)80年代常說,函數(shù)不該長于一屏。
那時候一屏只有20行,80列。
現(xiàn)在能顯示大概100行,150個字符,但是函數(shù)也不應(yīng)該占滿屏幕,20行封頂最佳。 - 代碼塊和縮進(jìn)
if語句、else語句、while語句等,其中代碼塊應(yīng)該只有一行。該行大抵應(yīng)該是一個函數(shù)調(diào)用語句。
這也意味著函數(shù)不應(yīng)該大到足以容納嵌套結(jié)構(gòu)。所以,函數(shù)的縮進(jìn)層級不該多于一層或兩層。
只做一件事
- 如果函數(shù)只是做了該函數(shù)名下同一抽象層上的步驟,則函數(shù)還是只做了一件事。
- 要判斷函數(shù)是否不止做了一件事,還有一個方法,就是看是否能再拆出一個函數(shù),該函數(shù)不僅只是單純地重新詮釋其實(shí)現(xiàn)。
- 只做一件事的函數(shù)無法被合理的切分為多個區(qū)段。
每個函數(shù)一個抽象層級
函數(shù)中混雜不同的抽象層級,往往讓人迷惑。一旦細(xì)節(jié)和基礎(chǔ)概念混雜,細(xì)節(jié)就會在函數(shù)中糾結(jié)起來。
要讓每個函數(shù)后面都跟著位于下一抽象層級的函數(shù)。
switch語句
問題:
- 太長,當(dāng)出現(xiàn)新的條件時,還會變得更長
- 不止做了一件事
- 違反了單一權(quán)責(zé)原則
- 違反了開閉原則,每當(dāng)添加新類型時,就必須修改。
解決方案:
將switch語句埋到抽象工廠底下。
使用描述性的名稱
- 函數(shù)越短小、功能越集中,就越便于取個好名字。
- 別害怕長名稱。
- 別害怕花時間取名字。
- 選擇描述性的名稱能理清關(guān)于模塊的設(shè)計(jì)思路,并幫助改進(jìn)之。追索好名稱,往往導(dǎo)致對代碼的改善重構(gòu)。
- 命名方式要保持一致。
函數(shù)參數(shù)
最理想的參數(shù)數(shù)量是零,其次是一,再次是二,應(yīng)盡量避免三參數(shù)函數(shù)。
- 參數(shù)不易對付。
它帶有太多概念性。以StringBuffer為例,作為參數(shù)傳遞時,讀者每次看到都得翻譯一遍。參數(shù)和函數(shù)名處于不同的抽象層級。 - 參數(shù)讓測試變的困難。
如果參數(shù)多余兩個,測試覆蓋所有可能的組合讓人生畏。 - 輸出參數(shù)比輸入?yún)?shù)還要難以理解
信息通過參數(shù)輸入函數(shù),通過返回值從函數(shù)輸出。盡量不要讓信息通過參數(shù)輸出。
一元函數(shù)的普遍形式
向函數(shù)傳入單個參數(shù)有兩種極普遍的的理由。
你也許會問關(guān)于那個參數(shù)的問題。也可能是操作參數(shù),將其轉(zhuǎn)換為其他什么東西,再輸出之。
還有一種不普遍但極有用的單參數(shù)函數(shù)形式,事件。這種形式,有輸入?yún)?shù)無輸出參數(shù)。小心使用這種形式。
盡量避免編寫不遵循這些形式的一元函數(shù)。例如,void includeSetupPageInto(StringBuffer pageText)。對于轉(zhuǎn)換,應(yīng)該使用返回值,而不應(yīng)該使用輸出參數(shù)。StringBuffer transform(StringBuffer in)要比void transform(StringBuffer out)強(qiáng),它遵循了轉(zhuǎn)換的形式。
標(biāo)識參數(shù)
向函數(shù)傳入布爾值是丑陋的做法。它直接告訴我們這個函數(shù)不止做一件事,傳入true將會這樣做,傳入false則會那樣做。
二元函數(shù)
盡管有些時候兩個參數(shù)剛好。比如Point p = new Point(0,0),笛卡爾點(diǎn)天生就有兩個參數(shù)。但是這是因?yàn)檫@兩個參數(shù)是單個值的有序組成部分。
其他的就不好了,即使是如assertEquals(expected,actual),這樣的二元函數(shù)也有問題。你會經(jīng)常搞錯這兩個參數(shù)的位置。這兩個參數(shù)沒用自然的順序。
盡量利用一些機(jī)制把二元函數(shù)轉(zhuǎn)換成一元函數(shù)。例如,writeFiled(outputStream,name),可以把writeField方法寫成outputStream的成員函數(shù),從而能這樣用:outputStream.writeField(name)?;蛘?,也可以把outputStream寫成當(dāng)前類的成員變量,從而無需再傳遞它。還可以分離出類似FieldWriter的新類,在其構(gòu)造器中采用outputSteam,并且包含一個write方法。
三元函數(shù)
排序、琢磨、忽略的問題都會加倍體現(xiàn)。
當(dāng)然也有一些函數(shù)寫成三參數(shù)還是可以不錯的。
參數(shù)對象
如果函數(shù)看來需要兩個、三個、或三個以上參數(shù),就說明其中一些參數(shù)應(yīng)該封裝為類類。當(dāng)一組參數(shù)被共同傳遞,往往就是該有自己名稱的某個概念的一部分。
參數(shù)列表
有時,我們需要向函數(shù)傳入數(shù)量可變的參數(shù)。例如,String.format方法。
但是這個方法實(shí)則是二元函數(shù)。
動詞與關(guān)鍵字
給函數(shù)取個好名字,能很好的解釋函數(shù)的意圖,以及參數(shù)的順序和意圖。把參數(shù)的名稱編碼成函數(shù)名。例如write(name)和writeField(name),它告訴我們name是一個field,assertEqual()改成assertExpectedEqualsActual(expected,actual)會好些。這大大減輕了記憶參數(shù)順序的負(fù)擔(dān)。