是什么使代碼 “Swifty”? —— Safe

盡管編程語(yǔ)言是由其語(yǔ)法正式定義的,但實(shí)際上在實(shí)踐中使用它們的方式還是可以由它們當(dāng)前的約定來(lái)確定的。畢竟,就語(yǔ)法而言,大多數(shù)受“ C影響 ” 的語(yǔ)言看起來(lái)都非常相似,以至于您可以用幾乎使它看起來(lái)像JavaScript,C?;駽本身的方式編寫Swift。

在Swift社區(qū)中,短語(yǔ) "Swifty code" 通常用于描述遵循當(dāng)前最流行的約定的代碼。但是,盡管Swift的核心語(yǔ)法自最初引入以來(lái)并沒有太大變化,但其約定隨著時(shí)間的推移發(fā)生了巨大變化。

例如,許多Swift開發(fā)人員都記得從Swift 2到Swift 3的轉(zhuǎn)換是語(yǔ)法方面的重大更改,但是這些更改中的大多數(shù)并不是真正的語(yǔ)法更改——它們是基于新集合對(duì)標(biāo)準(zhǔn)庫(kù)API的更改命名約定。加上Swift 4對(duì)關(guān)鍵路徑和Codable的介紹,Swift 5.1的函數(shù)生成器,屬性包裝器和不透明的返回類型,以及多年來(lái)引入的更多API和功能,并且開始變得很清楚,是什么使代碼 “swifty” 是一個(gè)不斷變化的目標(biāo)。

本周,讓我們仔細(xì)研究一下Swift的核心約定,以試圖回答是什么真正使代碼“ Swifty ” 的問題。
Swifty Code —— Safe

一致的目標(biāo)(Aligned Goals)

在某種程度上,上述問題的簡(jiǎn)單答案可能是“與Swift的核心目標(biāo)完全吻合的代碼”。畢竟,盡管Swift的各種API,約定和語(yǔ)言功能會(huì)隨著時(shí)間而變化,但它的基本目標(biāo)基本保持不變——因此,如果我們能夠以符合這些目標(biāo)的方式編寫自己的代碼,那么我們將有更好的機(jī)會(huì)在任何給定的Swift上下文中使我們的代碼看起來(lái)自然而清晰。

那么,這些目標(biāo)到底是什么?Swift的官方網(wǎng)站上的About頁(yè)面列出了三個(gè)關(guān)鍵字:

  • 安全(Safe):為了最大限度地減少開發(fā)人員的錯(cuò)誤;
  • 迅速(Fast):執(zhí)行的速度要快;
  • 表現(xiàn)力(Expressive):因?yàn)镾wift的目標(biāo)是盡可能清晰易懂。

是什么使代碼 “Swifty”? —— Fast 介紹了如何利用系統(tǒng)的一些內(nèi)置方法來(lái)提示性能
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達(dá)性命名和API設(shè)計(jì)傳達(dá)我們的代碼意圖

讓我們來(lái)看看一些不同的事情,這些事情可能要牢記在心,以便使我們自己的代碼遵循這些原則。

通過(guò)強(qiáng)大的類型安全保持清晰(Clarity through strong type safety)

讓我們從第一個(gè)關(guān)鍵字開始——安全(Safe)。Swift非常重視類型安全性這一事實(shí)不容忽視——它具有靜態(tài)類型檢查,強(qiáng)大的泛型系統(tǒng),以及編譯時(shí)需要執(zhí)行諸如類型擦除之類的操作才能使編譯器能夠驗(yàn)證我們的代碼結(jié)構(gòu)。

但是,遇到不是很明顯可以改善我們代碼的類型安全或使代碼更加“Swifty”的情況是很常見的,例如,這里我們根據(jù)筆記所屬的組的名稱存儲(chǔ)筆記的集合:

struct NoteCollection {
    var notesByGroup: [String : [Note]]
    ...
}

乍一看,上面的代碼似乎很完美。但是,在查看上面的聲明時(shí),一個(gè)細(xì)節(jié)一點(diǎn)都不明顯,那就是我們?nèi)绾翁幚砦捶纸M的值,以及如何處理包含用戶最近打開的所有便箋的特殊組——當(dāng)前是通過(guò)傳遞一個(gè)空字符串或使用“recents”字符串來(lái)完成的:

let groupedNotes = collection.notesByGroup["MyGroup"]
let ungroupedNotes = collection.notesByGroup[""]
let recentNotes = collection.notesByGroup["recent"]

盡管上述設(shè)計(jì)可能有完全合理的理由(例如,我們使用的結(jié)構(gòu)可能是通過(guò)網(wǎng)絡(luò)加載筆記時(shí)如何組織筆記的結(jié)構(gòu)),但這確實(shí)導(dǎo)致我們的某些調(diào)用變得非常隱秘——繞彎子會(huì)增加開發(fā)人員犯錯(cuò)的機(jī)會(huì)。很容易忘記,一個(gè)空字符串意味著應(yīng)該檢索所有未分組的筆記,如果用戶將其自定義組之一命名為“recent”會(huì)怎樣?

讓我們看看是否可以使上面的代碼更加安全,并使其更加“Swifty”。由于我們的notesByGroup字典具有三種不同的用例,因此,我們用一個(gè)自定義枚舉替換其基于字符串的鍵,該枚舉將這三種變體建模為不同的情形,如下所示:

enum Group: Hashable {
    case none
    case recent
    case named(String)
}

struct NoteCollection {
    var notesByGroup: [Group : [Note]]
    ...
}

上面的內(nèi)容看似微小的變化,但是它使我們的調(diào)用更加清晰,因?yàn)槲覀儸F(xiàn)在利用類型系統(tǒng)來(lái)區(qū)分三種獨(dú)立的組類型——所有這些都不會(huì)使我們的API變得更加復(fù)雜:

let groupedNotes = collection.notesByGroup[.named("MyGroup")]
let ungroupedNotes = collection.notesByGroup[.none]
let recentNotes = collection.notesByGroup[.recent]

這也許就是使代碼在類型安全方面“Swifty”的本質(zhì)。雖然有很多方法可以使API真正變得復(fù)雜以使其更加類型安全,但竅門是使用Swift的語(yǔ)言功能找到一種增加該類型安全性的方法,而又不會(huì)使我們的代碼難以理解或使用。

雖然通常使用類型安全性來(lái)防止將類型B的值錯(cuò)誤地傳遞給接受A的API,但是強(qiáng)類型化通常也提供了一種改善我們代碼的語(yǔ)義和邏輯的方法。在下面的示例中,我們的代碼在技術(shù)上是類型安全的——因?yàn)槲覀冋谑褂肧wift的泛型功能來(lái)實(shí)現(xiàn)LoadingOperation,該LoadOperation可以加載符合Loadable協(xié)議的任何資源:

class LoadingOperation<Resource: Loadable> {
    private let resource: Resource

    init(resource: Resource) {
        self.resource = resource

        if let preloadable = resource as? Preloadable {
            preloadable.preload()
        }
    }
    
    ...
}

但是,我們有條件地強(qiáng)制轉(zhuǎn)換資源以查看其是否也符合Preloadable(如果是,則預(yù)加載該資源)這一事實(shí)可以說(shuō)有點(diǎn)奇怪。上面的實(shí)現(xiàn)不僅使我們很難理解如何進(jìn)行資源預(yù)加載(因?yàn)轭愋拖到y(tǒng)沒有給我們?nèi)魏伟凳疚覀儜?yīng)該遵循Preloadable的提示,以使這種情況發(fā)生),而且這樣做非常不直觀預(yù)加載是初始化操作的副作用

作為替代,讓我們預(yù)加載一個(gè)明確的API,該API僅在操作的Resource符合Preloadable時(shí)才可用,如下所示:

extension LoadingOperation where Resource: Preloadable {
    func preload() {
        resource.preload()
    }
}

上面的更改都使我們更加清楚了預(yù)加載資源的條件,并且現(xiàn)在我們可以從初始化程序中消除類型轉(zhuǎn)換的副作用——大贏家!需要注意的重要一點(diǎn)是,從安全角度出發(fā)編寫“ Swifty”代碼絕對(duì)不是盡可能多地使用泛型。而是要有選擇地使用類型系統(tǒng)的各個(gè)方面和功能,以使我們的代碼更易于理解和使用(更難于濫用)。

文章來(lái)自 John SundellWhat makes code “Swifty”?中關(guān)于Safe的內(nèi)容

是什么使代碼 “Swifty”? —— Fast 介紹了如何利用系統(tǒng)的一些內(nèi)置方法來(lái)提示性能
是什么使代碼 “Swifty”? —— Expressive 介紹了如何使用表達(dá)性命名和API設(shè)計(jì)傳達(dá)我們的代碼意圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容