命名
- 名副其實(shí):避免出現(xiàn)命名模糊,應(yīng)做到見名知其意
// bad naming
func getThem() -> [Int] {
var list: [Int] = []
for x in 0...99 {
list.append(x)
}
return list
}
// good naming
func getFlaggedCells() {
var cells: [Int] = []
let gameBoard = 0...99
for cell in gameBoard {
cells.append(cell)
}
return cells
}
- 避免誤導(dǎo): 比如前后拼寫不一致
/// bad
func testName() {
var a = 1
let o = 1
let o1 = 2
let I = 2
if o == 1 {
a = o1
} else {
I = o1s
}
}
- 做有意義的區(qū)分,讀者能夠通過命名就能分別出兩個(gè)方法之間的區(qū)別
-
使用讀得出來的名稱,不要造詞
-
使用可搜索的名稱,比如避免使用magic number
- 不推薦使用成員前綴
- 避免思維映射:不應(yīng)當(dāng)讓讀者在鬧中把你的名稱翻譯為他們熟知的名稱
- 類名:類名應(yīng)該是名詞,不應(yīng)當(dāng)時(shí)動(dòng)詞
- 方法名:應(yīng)當(dāng)是動(dòng)詞或者動(dòng)詞短語,eg: postPayment, delelePage
- 避免同一個(gè)單詞用于不同目的
- 使用用專業(yè)術(shù)語命名
- 添加有意義的語境:主要用于拆分大函數(shù)
函數(shù)
- 目標(biāo):如何構(gòu)建讓讀者一看就明白的函數(shù)
- 短?。汉瘮?shù)的第一個(gè)規(guī)則就是短小
- 一個(gè)函數(shù)只做一件事:函數(shù)應(yīng)該值做一件事,做好這件事,只做一件事。判斷一個(gè)函數(shù)是否只做了一件事:就是看是否能夠再拆出一個(gè)函數(shù)。
- 使用描述性的名稱定義函數(shù)
- 函數(shù)參數(shù):最理想的是零參數(shù),其次是一個(gè)參數(shù),兩個(gè)參數(shù),。。。如果要函數(shù)要對輸入?yún)?shù)進(jìn)行轉(zhuǎn)換操作,轉(zhuǎn)換結(jié)果就該體現(xiàn)為返回值
- 標(biāo)識參數(shù):標(biāo)識參數(shù)丑陋不堪。reader(Boolean isSuite) 可以寫為renderForSuite()和renderForSingleTest()
- 二元函數(shù)(兩個(gè)參數(shù)的函數(shù)):可以使用一些機(jī)制將其轉(zhuǎn)換為一元函數(shù) writeField(outputStream, name) => outputStream.writeField(name)
- 三元函數(shù)(三個(gè)參數(shù)):應(yīng)轉(zhuǎn)換為參數(shù)對象
- 參數(shù)對象:函數(shù)中有三個(gè)及以上的參數(shù),應(yīng)將他們封裝為對象
- 動(dòng)詞與關(guān)鍵字:函數(shù)和參數(shù)應(yīng)當(dāng)形成一種非常良好的動(dòng)詞/名詞對形式
- 無副作用:一個(gè)函數(shù)只做一件事,eg:一個(gè)做加法的函數(shù),卻在函數(shù)內(nèi)部修改了全局變量
- 分隔指令與詢問:指令與詢問分隔開來,防止混淆的發(fā)生
// 錯(cuò)誤的
if (set("username", "bob")) ...
// 正確的
if (attributeExist("username") {
setAttribute("username", "bob")
}
- 使用異常替代返回錯(cuò)誤碼
- 抽離tryCatch代碼塊
- 錯(cuò)誤處理就是一件事:錯(cuò)誤處理的函數(shù)只做錯(cuò)誤處理,不做其他事
- 別重復(fù)自己:比如避免類似于這種寫法:Setup ,SuiteSetup, TeadDown, SuiteTearDown
- 結(jié)構(gòu)化編程
注釋
- 注釋不能美化糟糕的代碼:帶有少量注釋的整潔而有表達(dá)力的代碼,要比帶有大量注釋的零碎而復(fù)雜的代碼像樣得多,不能因?yàn)榇a很糟糕而通過寫注釋來解釋
- 有代碼來闡述:很多時(shí)候,簡單到只需要?jiǎng)?chuàng)建一個(gè)描述與注釋所言同一事物的函數(shù)即可
- 好注釋包含哪些:
- 法律信息
- 提供信息的注釋
- 對意圖的解釋
- 闡述:注釋某些晦澀難明的參數(shù)或返回值的意義翻譯為某種可讀形式
- 警示:用于警告其他程序員會(huì)出現(xiàn)某種后果的注釋也是有用的
- TODO注釋:是一種程序員認(rèn)為應(yīng)該做的,但是由于某種原因目前還沒做的工作
- 放大:注釋可以用來放大某種看來不合理之物的重要性
- 壞的注釋有哪些:
- 楠楠自語
- 多余的注釋
- 誤導(dǎo)性的注釋
- 循規(guī)式注釋
- 日志型注釋
- 廢話注釋
- 位置標(biāo)記
- 括號后面的注釋:會(huì)破壞函數(shù)結(jié)構(gòu)
- 歸屬于署名:最好使用代碼版本控制來處理歸屬
- 注釋掉的代碼:直接把代碼刪掉即可
格式
- 格式的目的:關(guān)乎團(tuán)隊(duì)溝通,增強(qiáng)代碼的可讀性,可讀性會(huì)影響到代碼的可維護(hù)性和擴(kuò)展性
- 垂直格式
- 決定了代碼文件的行數(shù)
- 向報(bào)紙學(xué)習(xí):頭條主題 -> 大綱 -> 概述 -> 故事細(xì)節(jié)。篇幅:短小精悍,有些稍微有點(diǎn)長,
- 概念間垂直方向上的區(qū)隔
- 垂直方向上的靠近
- 垂直距離
- 變量聲明,應(yīng)盡可能靠近其使用位置
- 實(shí)體變量:應(yīng)該在類的頂部聲明
- 相關(guān)函數(shù):調(diào)用者函數(shù)應(yīng)該盡可能放在被調(diào)用者上面
- 概念相關(guān):概念相關(guān)的代碼應(yīng)該放到一起,相關(guān)性越強(qiáng),彼此之間的距離就應(yīng)該越短
- 垂直順序
- 決定了代碼的寬度
- 橫向格式:代碼行長度控制在100到120個(gè)字符之間
- 水平方向的區(qū)隔與靠近
- 水平對齊:推薦不水平對齊,不推薦C++的那種水平對齊方式
- 縮進(jìn):類中的方法相對改類縮進(jìn)一個(gè)層級,方法的細(xì)實(shí)現(xiàn)相對方法聲明縮進(jìn)一個(gè)層級,代碼塊的事項(xiàng)相對于容器代碼塊縮進(jìn)一個(gè)層級
對象和數(shù)據(jù)結(jié)構(gòu)
- 數(shù)據(jù)抽象:不愿保羅數(shù)據(jù)細(xì)節(jié),更愿意以抽象形態(tài)表述數(shù)據(jù)
- 數(shù)據(jù),對象的反對稱性
- 過程式代碼便于在不改動(dòng)既有數(shù)據(jù)結(jié)構(gòu)的前提下添加新函數(shù),面向?qū)ο蟠a便于在不改動(dòng)既有函數(shù)的前提下添加新類
錯(cuò)誤處理
- 當(dāng)錯(cuò)誤發(fā)生時(shí),程序員就有責(zé)任確保代碼照常工作
- 使用異常而非返回碼:如果使用返回碼:調(diào)用者必須在調(diào)用之后即刻檢查錯(cuò)誤,但是這個(gè)步驟很容易被遺忘。如果是拋出異常,調(diào)用代碼就被編譯識別出來,不如不處理異常就編譯不通過
- 先寫try-catch-finally
- 使用不可控異常
- 給出異常發(fā)生的環(huán)境說明
- 依調(diào)用者需要定義異常類:便于被捕獲
- 別返回null值
- 別傳遞null值
單元測試
- TDD三定律
- 定律一:在編寫不能通過的單元測試前,不可編寫生產(chǎn)代碼
- 定律二:只可編寫剛好無法通過的單元測試,不能編譯也算不通過
- 定律三:只可編寫剛好足以通過當(dāng)前失敗測試的生產(chǎn)代碼
- 保持測試整潔:測試代碼和生成代碼一樣重要,它需要被思考,被設(shè)計(jì)和被找了,它應(yīng)該想生成代碼一般保持整潔。測試越臟,代碼就會(huì)變得越臟。最終,丟了測試,代碼開始腐敗
- 整潔的測試
- 三個(gè)要素:可讀性,可讀性,可讀性
- 測試的三個(gè)環(huán)節(jié):構(gòu)造-操作-檢驗(yàn) (BUILD-OPERATION-CHECK)
- 每個(gè)測試一個(gè)斷言:單個(gè)測試中的斷言數(shù)量應(yīng)該最小化
- 每個(gè)測試一個(gè)概念
- 最佳規(guī)則:應(yīng)該盡可能減少每個(gè)概念的斷言數(shù)量,每個(gè)測試函數(shù)只測試一個(gè)概念
- FIRST:
- 快速Fast:測試應(yīng)該足夠快
- 獨(dú)立Independent:測試應(yīng)該相互獨(dú)立
- 可重復(fù)性repeatable:測試在任何環(huán)境中重復(fù)通過
- 自足驗(yàn)證Self-Validating:測試應(yīng)該有布爾值輸出
- 及時(shí)Timely:測試應(yīng)及時(shí)編寫
- FIRST:
類
- 類應(yīng)該短小
- 單一權(quán)則原則:類和模塊應(yīng)有且只有一條加以修改的理由
- 內(nèi)聚:類應(yīng)該只有少量實(shí)體變量。類中的每個(gè)方法都操作一個(gè)或多個(gè)這種變量,通常而言,方法操作的變量越多,就越粘聚到類上。如果一個(gè)類中的每個(gè)變量都被每個(gè)方法所使用,則該類具有最大的內(nèi)聚性。
- 為了修改而組織:在整潔的系統(tǒng)中,我們對類加以組織,以降低修改的風(fēng)險(xiǎn)。在理想系統(tǒng)中,我們通過擴(kuò)展系統(tǒng)而非修改現(xiàn)有代碼來添加新特性
- 隔離修改:接口代替類
系統(tǒng)
- 將系統(tǒng)的構(gòu)造與使用分開:軟件系統(tǒng)應(yīng)將啟動(dòng)過程和啟動(dòng)過程之后的運(yùn)行時(shí)邏輯分離開,在啟動(dòng)中構(gòu)建應(yīng)用對象,也會(huì)存在互相纏結(jié)的依賴關(guān)系
- 分解main函數(shù):將構(gòu)造與適使用分開的方法之一是將全部構(gòu)造過程搬遷到main或稱之為main的模塊之中。main函數(shù)創(chuàng)建系統(tǒng)所需的對象,再傳遞給應(yīng)用程序,應(yīng)用程序只管使用。
- 工廠:使用抽象工廠模式讓應(yīng)用程雪控制何時(shí)創(chuàng)建,但構(gòu)造的細(xì)節(jié)卻隔離與應(yīng)用程序之外
- 依賴注入(DI):分離構(gòu)造與使用,在依賴管理情景中,對象不應(yīng)該負(fù)責(zé)實(shí)例化對自身的依賴,應(yīng)該將這份權(quán)責(zé)移交給其他”有權(quán)力”的機(jī)制,從而實(shí)現(xiàn)控制的反轉(zhuǎn)
- 擴(kuò)容:架構(gòu)都可以遞增式地增長,只要我們持續(xù)將關(guān)注面恰當(dāng)?shù)厍蟹?/li>
- 測試驅(qū)動(dòng)系統(tǒng)架構(gòu):最佳的系統(tǒng)架構(gòu)由模塊化的關(guān)注面領(lǐng)域組成,每個(gè)關(guān)注面均用純Java對象實(shí)現(xiàn),不同的領(lǐng)域之間最不具有侵害性的方面或類方面工具整合起來,這種架構(gòu)能測試驅(qū)動(dòng),就像代碼一樣
迭進(jìn)
-
簡單設(shè)計(jì)的四條原則
- 運(yùn)行所有測試
- 不可重復(fù)
- 表達(dá)程序員的意圖(保證表達(dá)力)
- 盡可能減少類和方法的數(shù)量
-
簡單設(shè)計(jì)規(guī)則1:運(yùn)行所有的測試
- 全面測試并持續(xù)通過所有測試的系統(tǒng),就是可測試的系統(tǒng)
- 遵循有關(guān)編寫測試并持續(xù)運(yùn)行測試的簡單,明確的準(zhǔn)則,系統(tǒng)就會(huì)更貼近低耦合度,高內(nèi)聚的目標(biāo)。編寫測試引致更好的設(shè)計(jì)
- 測試消除了對清理代碼就會(huì)破壞代碼的恐懼
- 通過實(shí)例起到文檔作用
-
簡單設(shè)計(jì)規(guī)則2~4:重構(gòu)
- 重構(gòu)過程中,可以應(yīng)用有關(guān)優(yōu)秀設(shè)計(jì)的一切知識。提升內(nèi)聚性,降低耦合度,切分關(guān)注面,模塊化系統(tǒng)性關(guān)注面,縮小函數(shù)和類的尺寸,選用更好的名稱。
不可重復(fù):重復(fù)是擁有良好設(shè)計(jì)系統(tǒng)的大敵。它代表著額外的工作,額外的風(fēng)險(xiǎn)和額外且不必要的復(fù)雜度。
表達(dá)力:下一位讀代碼的人最有可能是自己。選用較好的名稱,將大函數(shù)切分小函數(shù),時(shí)時(shí)照拂自己創(chuàng)建的東西。用心是最珍貴的資源
極可能少的類和方法(優(yōu)先級最低)

