- 原文地址:Function Naming In Swift 3
- 原文作者:Pablo Villar
- 譯文出自:掘金翻譯計劃
- 譯者:Zheaoli
- 校對者:Kulbear, Tuccuay
昨天,我開始將這個 Jayme 遷移到 Swift 3。這是我第一次將一個項目從 Swift 2.2 遷移至 Swift 3。說實話這個過程十分的繁瑣,由于 Swift 3 在老版本基礎上發(fā)生了很多比較大的改變,我不得不承認眼前這樣一個事實,除了花費較多的時間以外,沒有其余的捷徑可走。不過這樣的經(jīng)歷也帶來一點好處:我對 Swift 3 的理解變得更為深入,對我來講,這可能是最好的消息了。?
在遷移代碼的過程中,我需要做出很多的選擇。更為蛋疼的是,整個遷移過程并不是修改代碼那么簡單,你還需要用耐心去一點點適應 Swift 3 中帶來的新變化。某種意義上來講,修改代碼只是整個遷移過程的開始而已。
如果你已經(jīng)決定將你的代碼遷移到 Swift 3 ,我建議你去看看這篇文章來作為你萬里長征的第一步。
如果一切順利的話,在不久以后,我將回去寫一篇博客來記錄下整個遷移過程中的點點滴滴,包括我所作出的決定等等。但是眼前,我將會把注意力集中在一個非常非常重要的問題上:怎樣正確的編寫函數(shù)簽名.
首先,讓我們來看看在 Swift 3 與 Swift 2 相比函數(shù)命名方式的差異吧。
在 Swift 2 中,函數(shù)中的第一個參數(shù)的標簽在調用時可以省略,這是為了遵循這樣一個 good ol’ Objective-C conventions 標準。比如我們可以這樣寫代碼:
// Swift 2 func handleError(error: NSError) { } let error = NSError() handleError(error) // Looks like Objective-C在 Swift 3 中調用函數(shù)時,其實也是有辦法省略第一個參數(shù)的標簽的,但默認情況下不是這樣:
// Swift 3 func handleError(error: NSError) { } let error = NSError() handleError(error) // Does not compile! // ? Missing argument label 'error:' in call當遇到這樣的情況時,我們第一反應可能是下面這樣的:
// Swift 3 func handleError(error: NSError) { } let error = NSError() handleError(error: error) // Had to write 'error' three times in a row! // My eyes already hurt ?
當然如果這樣做,你肯定會很快意識到你的代碼將將會變得有多坑爹。
如同前面所說的一樣,在 Swift 3 中,我們是可以在調用函數(shù)時,將第一個參數(shù)的標簽省略的,但是記住,你要去明確的告訴編譯器這一點:
// Swift 3 func handleError(_ error: NSError) { } // ? Notice the underscore! let error = NSError() handleError(error) // Same as in Swift 2
你可能在使用 Xcode 自帶的遷移工具進行遷移時遇到這樣的情況。
注意,在函數(shù)簽名中的下劃線的意思是:告訴編譯器,我們在調用函數(shù)時第一個參數(shù)不需要外帶標簽。這樣,我們可以按照 Swift 2 中的方式去調用函數(shù)。
此外,你需要意識到,Swift 3 之所以修改了函數(shù)編寫方式,是為了保證其一致性與可讀性:我們不在需要對不同的參數(shù)區(qū)別對待。我想這可能是你遇到的第一個問題。
好了,現(xiàn)在代碼可以編譯運行了,但是你必須知道,你需要反復的去閱讀 Swift 3 API design guidelines 一文。
?? 一點微小的人生經(jīng)驗:你需要隨時去誦讀 Swift 3 API design guidelines 一文,這會為你解鎖 Swift 開發(fā)的新體位。
讓我們再來看看之前的代碼:
為了精簡我們的代碼,你可以將你的代碼進行修剪一番,比如去除函數(shù)名里的類型信息等。
// Swift 3 func handle(_ error: NSError) { /* ... */ } let error = NSError() handle(error) // Type name has been pruned // from function name, since it was redundant
如果你想讓你的代碼變得更短,更精悍,更明了的話,我給你們講,作為一個欽定的開發(fā)者,一定要去反復誦讀這篇 Swift 3 API design guidelines 文章到可以默寫為止。
要注意讓函數(shù)的調用過程是清晰、明確的,我們根據(jù)以下兩點來確定函數(shù)的的命名和參數(shù):
現(xiàn)在請睜大眼睛看清楚我們下面所討論的東西。 ??
上面我們所講的東西并沒有包括所有可能出現(xiàn)的情況,換句話說,你可能遇到這樣一種特殊情況,即,一個參數(shù)的類型沒有辦法直觀的體現(xiàn)其作用。
讓我們考慮下面這樣一種情況:
// Swift 2 func requestForPath(path: String) -> URLRequest { } let request = requestForPath("local:80/users")如果你想將代碼遷移到 Swift 3 ,那么根據(jù)已有的知識,你可能會這么做:
// Swift 3 func request(_ path: String) -> URLRequest { } let request = request("local:80/users")講真,這段代碼看起來可讀性很差,讓我們稍微修改下:
// Swift 3 func request(for path: String) -> URLRequest { } let request = request(for: "local:80/users")
OK,現(xiàn)在看起來舒服多了,但是并沒有解決我上面提到的問題。
在我們調用這個函數(shù)的時候,我們怎樣很直觀的知道我們需要給這個參數(shù)傳遞一個 Web Url 呢?你所能提前知道的是你需要傳遞一個 String 類型的變量進去,但是你并不清楚你需要傳遞一個 Web Url 進去。
同理,我們在一個大型項目中,我們需要很清楚的明白每個參數(shù)的作用所在,但是很明顯,目前我們還沒有解決這個大問題,比如:
?? 綜上,我給你們一點微小的人生經(jīng)驗吧: 謹慎精簡你的代碼 ?
回到代碼上,我們可以給參數(shù)添加上相對應的標簽來解決這個問題,好了看看下面這個代碼:
func request(forPath path: String) -> URLRequest { } let request = request(forPath: "local:80/users")
好了,現(xiàn)在代碼看起來是不是更清楚,可讀性更強了呢? ? 恭喜~
講真,看到這里其實你可以關閉瀏覽器了,但是事實上,下面才是最精華的部分。
好了,讓我們來看看關于函數(shù)參命名的用詞問題:
func request(forPath path: String) -> URLRequest { } // The word 'path' appears twice
這段代碼看起來不錯,但是如果你想讓其變得更好,那么請看接下來的部分。
這個小技巧很簡單:在上下文中反映參數(shù)的類型及作用,這樣你就可以無腦的精簡你的代碼了。
吶,我們來看看下面這段代碼。
typealias Path = String // To the rescue! func request(for path: Path) -> URLRequest { } let request = request(for: "local:80/users")
在這個例子中,參數(shù)的類型和參數(shù)的作用表達達成了一個完美的統(tǒng)一,因為你在上下文中為String 賦予了一個別名叫做 Path。
現(xiàn)在,你的函數(shù)看起來還是依舊的精簡,可讀性較高,但是卻不重復。
以此類推,你可以使用同樣的方式來書寫一些優(yōu)美的代碼,比如:
typealias Path = String typealias StatusCode = Int typealias HTTPHeader = [String: String] // etc...
如你所見,你可以盡情的寫精簡而優(yōu)美的代碼了。
不過,請記住,凡事走向極端便變了味了:這個小技巧會為你的代碼添加額外的負擔,特別是你們代碼存在多重嵌套的情況下。因此請記住,如果你無腦的使用這樣的小技巧的話,那么你可能會付出一些慘痛的代價。
很多時候,你在使用 Swift 3 時,命名函數(shù)的時候你會遇到很多困難。
積累一些代碼片段可能會幫助你很多:
func remove(at position: Index) -> Element { } employees.remove(at: x) func remove(_ member: Element) -> Element? { } allViews.remove(cancelButton) func url(forPath path: String) -> URL { } let url = url(forPath: "local:80/users") typealias Path = String // Alternative func url(for path: Path) -> URL { } let url = url(for: "local:80/users") func entity(from dictionary: [String: Any]) -> Entity { /* ... */ } let entity = entity(from: ["id": "1", "name": "John"])