本文翻譯自 Making your iOS application easy to read with these simple steps.ios
優秀的程序員會用盡量簡單的方式來解釋他們的代碼,即便是物理學家均可以用一張白紙和一隻鉛筆來解釋蟲洞,咱們又未嘗不可?程序員
我會盡量讓代碼寫地簡單、易讀,包括選擇合適的變量名、使用編碼規範(code conventions)等等,但仍是缺了點東西,理解代碼不該該是去理解「如何」實現的,而是要理解想要「達成」什麼。bash
甚至能夠說要讓讀代碼像讀小說同樣,而不是一大堆代碼。app
下面討論三大主題:ide
閱讀其餘人的代碼可能會很是折磨,若是不提供合適的上下文,咱們會迷失在尋找某個函數或屬性的意義中。函數
不管是二進制語言、低級語言仍是高級語言,語法都在變得愈來愈友好,以便吸引更多開發者。而隨着語法變得更接近英語,咱們的代碼也應該簡明扼要、不言自明。佈局
寫出良好的代碼,讀起來像小說同樣,容易閱讀和理解(即便沒有給出上下文)。ui
正確的方式是:編碼
咱們寫函數時都會假設閱讀它的人擁有足夠的上下文,可以理解函數想實現什麼。用模糊的含義來命名函數,例如「handleRedView()」會引發不少問題,」RedView」是啥?這個函數主要是想幹啥?spa
因此在某些狀況下函數的用途會很模糊,若是沒有提供足夠的上下文,閱讀起來會很是困難
咱們能夠把函數的用途分爲四類:
一般會觸發路由/管理函數,例子以下:
delegate.dataHasUpdated()
func dataHasUpdated()
{
//通知某事的發生
}
// 通知函數
override func engineStarted()
{
super.engineStarted()
handleCarStarted()
}
複製代碼
回調函數,通知某事已經/即將發生,並給機會進行響應。
大部分狀況下用於被代理(delegate)觸發的操做,或是通知(notification)處理函數。
用於聯合多個函數以實現更高級的用途,不須要依賴參數,block 中的全部代碼都會執行。
// 管理函數
func handleCarStarted()
{
turnLights(on: true)
turnAC(on: true)
}
複製代碼
上面的函數包含全部須要的信息,汽車啓動時執行這些函數,此時咱們不關注這是「如何」實現的,而是關注它作了「什麼」。
用於聯合多個函數以實現更高級的用途,須要依賴一些參數,只在咱們想執行的時候才執行。
// 路由函數
private func turnLights(on shouldTurnLightsOn: Bool)
{
if shouldTurnLightsOn
{
turnExteriorLightsOn()
checkForBurnedBulbs()
}
else { turnExteriorLightsOff() }
若是 if 語句只執行一件事,我喜歡把 "if" "else" 和執行語句寫在同一行,這樣代碼讀起來會更流暢。
複製代碼
路由函數一般同於指向執行函數,但在某些狀況下,若是邏輯代碼不超過一行,也能夠包含本身的邏輯。
函數名字的具體實現。
// 執行函數
private func turnExteriorLightsOn()
{
leftFrontLight.isOn = true
rightFrontLight.isOn = true
leftBackLight.isOn = true
rightBackLight.isOn = true
}
private func checkForBurnedBulbs()
{
for lightBulb in bulbs where !lightBulb.isUseable
{
Dashboard.(errorType: .lights)
break
}
}
複製代碼
這樣就能寫出一個乾淨、簡短的類,可讀性強,容易維護。
避免在函數名稱裏使用」and「: playAndMinimize() loadAndPlay() 這個壞習慣會打破單一責任原則,寫出可以適應兩種狀況的代碼。
避免在函數名稱裏進行猜想: moveRedViewIfNeeded() 上面的例子會致使後面的程序員必須深刻此函數,才能理解觸發移動 Red View 的時機,這樣不夠清晰。
不,layoutIfNeeded() 並不屬於這種狀況,由於咱們知道若是某個 view 的 setNeedDisplay 爲 true,就應該從新佈局。這種狀況在 Swift 語言裏很廣泛,由於函數基本上都是應用私有的。
談及代碼可讀性,我首先會想到編碼規範(code convention),它們被廣泛接受、應用普遍,但使用編碼規範並不必定會提高代碼質量,雖然有跨應用性但可讀性更差。
」is「前綴應該用於布爾型變量和方法,以便解釋返回值是布爾類型的。#編碼規範
既然」if「語句老是用於布爾值,那還有必要給每一個布爾屬性都加上」is「嗎?爲何蘋果要把 Swift 語法從 view.hidden 改爲 view.isHidden?我只能想到一種答案……由於**「if view.isHidden」**看起來更天然。
嘗試以如下原則使用「is」前綴:
public var isHidden: Bool
{
return alpha == 0.0
}
if containerView.isHidden
複製代碼
private var positionedVerticaly: Bool
{
return view.frame.width/2 == centerX
}
if positionedVerticaly
if positionedVerticaly && positionedHorizontally
VS
if isPositionedVerticaly
if isPositionedVerticaly && isPositionedHorizontally
複製代碼
public var isPositionedVerticaly: Bool
{
return positionedVerticaly
}
if containerView.isPositionedVerticaly
複製代碼
雖然用私有 set 並公開使用該屬性也是能夠的,但上面這種方式能夠實現封裝(encapsulation)。
封裝用於隱藏類中結構化數據對象的值或狀態,防止未受權方直接訪問。
那若是不是本身直接處理的布爾型,若是如何命名呢?
private var playerIsPlaying: Bool
private var gridConstraintIsEnabled()
「is」 須要指向某個東西:view.isHidden, 「is」指向 view。上面的例子裏使用了此原則,playerIsPlaying,「is」指向 player。
謹記:開發者一般會在閱讀屬性聲明以前先閱讀函數內部的代碼,嘗試搞明白這些屬性的用途。
/if playerIsPlaying { }/ 對比 /if isPlayerIsPlaying {}/
複製代碼
哪一個更天然?我想你已經有答案了。