Swift API 設(shè)計(jì)指南(上)

本文翻譯自蘋果官方文檔:Swift API Design Guidelines肴颊,如有錯(cuò)漏渣磷,歡迎指出。

基本準(zhǔn)則

  • 在調(diào)用處表意足夠明確是你最重要的目的竟宋。像方法和屬性這樣的實(shí)體(Entities)只聲明一次,但卻會(huì)被重復(fù)調(diào)用丘侠,所以你需要設(shè)計(jì)好你的 API 讓它們可以被明確和簡(jiǎn)潔的調(diào)用。當(dāng)我們?cè)u(píng)價(jià)某個(gè)設(shè)計(jì)的時(shí)候打肝,往往需要查看它的使用場(chǎng)景以確保它在實(shí)際環(huán)境中足夠明確挪捕,而不僅僅是看一眼它的聲明。

  • ** 明確比簡(jiǎn)潔更重要断医。**雖然 Swift 代碼可以寫得非常簡(jiǎn)潔奏纪,但是通過減少字符數(shù)使得代碼盡可能簡(jiǎn)短卻從不是我們的目標(biāo)。在 Swift 中序调,簡(jiǎn)潔只是強(qiáng)類型系統(tǒng)和其它可以減少樣板代碼的特性所帶來的一個(gè)副作用(side-effect)。

  • 為每個(gè)聲明編寫文檔注釋荣挨。寫文檔時(shí)的感悟會(huì)對(duì)你的設(shè)計(jì)產(chǎn)生重大影響朴摊,所以不要擱置它。

命名

促使能被明確調(diào)用

  • 包含所有需要的單詞口锭,以避免人們?cè)陂喿x調(diào)用處的代碼時(shí)感到困惑介杆。
    譬如,有一個(gè)方法春哨,要在集合(collection)中移除指定位置的元素。
    推薦
extension List {
    public mutating func remove(at position: Index) -> Element
}
employees.remove(at: x)

如果我們刪掉方法簽名中的at椰拒,那就給人一種該方法是搜索并刪除集合中等于x 的元素的感覺,而不是用x來指示元素在集合中的位置褒脯,并把該位置的元素刪除缆毁。
不推薦:

employees.remove(x) // unclear: are we removing x?
  • 刪除不需要的單詞。名字中的每個(gè)單詞都應(yīng)該在調(diào)用處傳達(dá)出重點(diǎn)信息脊框。
    更多的單詞或許能澄清意圖和消除歧義浇雹,但是那些讀者已經(jīng)知道的冗余信息都可以刪掉,尤其是那些僅僅重復(fù)了類型信息的單詞箫爷。
    不推薦:
public mutating func removeElement(member: Element) -> Element?
allViews.removeElement(cancelButton)

上述情況下聂儒,Element在調(diào)用處沒有提供任何要點(diǎn)信息,如下 API 會(huì)更好窜护。
推薦

public mutating func remove(member: Element) -> Element?
allViews.remove(cancelButton) // clearer

個(gè)別情況下非春,重復(fù)類型信息對(duì)于消除歧義是必要的,但一般來說护侮,用一個(gè)表明參數(shù)角色(role)而不是類型的詞储耐,會(huì)更好一些。詳情請(qǐng)參看下一條什湘。
基于變量、參數(shù)得哆、關(guān)聯(lián)類型的角色來對(duì)它們進(jìn)行命名哟旗,而不是基于它們的類型栋操。
不推薦

var string = "Hello"
protocol ViewController {
    associatedtype ViewType : View
}
class ProductionLine {
    func restock(from widgetFactory: WidgetFactory)
}

像這樣重申一遍類型名并不能最大程度提升明確性和表現(xiàn)力乐设。相反,我們應(yīng)該盡量選用那些表明實(shí)體角色的名字蠕啄。
推薦

var greeting = "Hello"
protocol ViewController {
    associatedtype ContentView : View
}
class ProductionLine {
    func restock(from supplier: WidgetFactory)
}

如果某個(gè)關(guān)聯(lián)類型和它的協(xié)議聯(lián)系非常緊密戈锻,以至于它的協(xié)議名就是它的角色名,那就給關(guān)聯(lián)類型的名字加上Type避免沖突:

protocol Sequence {
    associatedtype IteratorType : Iterator
}
  • 為弱類型信息的參數(shù)添加補(bǔ)充信息以表明參數(shù)的角色
    當(dāng)參數(shù)類型是NSObject哈街、Any拒迅、 AnyObject或者像Int、String這樣的基本類型的時(shí)候作箍,調(diào)用處的類型信息和上下文環(huán)境可能不能完全表明函數(shù)的意圖前硫。如下這個(gè)例子,它的聲明可能是明確的屹电,但在調(diào)用的地方就顯得意圖不明了。
    不推薦
func add(observer: NSObject, for keyPath: String)
grid.add(self, for: graphics) // vague

為了恢復(fù)明確性牧愁,在每個(gè)弱類型參數(shù)前加一個(gè)名詞用來描述它的角色外莲。
推薦

func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics) // clear

為能被流暢調(diào)用而努力

  • 盡量使方法或函數(shù)名在調(diào)用的時(shí)候符合英語語法規(guī)范
    推薦
x.insert(y, at: z)          “x, insert y at z”
x.subViews(havingColor: y)  “x's subviews having color y”
x.capitalizingNouns()       “x, capitalizing nouns”

不推薦

x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()

為了流暢性办龄,可以把調(diào)用時(shí)非重點(diǎn)的參數(shù)放到第一或者第二個(gè)參數(shù)之后淋昭。

AudioUnit.instantiate(
    with: description, 
    options: [.inProcess], completionHandler: stopProgressBar)
  • 工廠方法的命名以make開頭,譬如:x.makeIterator()英融。

  • 構(gòu)造方法和工廠方法在調(diào)用時(shí)應(yīng)該從一個(gè)不包含 first argument(譯者注:翻譯成第一個(gè)參數(shù)在這里好像不對(duì)頭,索性就不翻了驶悟,大家根據(jù)下面的例子應(yīng)該可以理解它的意思)的短語開始痕鳍,譬如:x.makeWidget(cogCount: 47)
    舉個(gè)例子笼呆,如下這些調(diào)用的短語都不包含 first argument。
    推薦

let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)

而下面這段代碼的 API 作者企圖用 first argument 創(chuàng)建符合英語語法的順暢 API:
不推薦

let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)

事實(shí)上汗茄,本指南包含了參數(shù)標(biāo)簽(argument labels铭若,譯者注:應(yīng)該和外部參數(shù)名一個(gè)意思吧)這樣的的主題,意味著第一個(gè)參數(shù)都應(yīng)該包含一個(gè)標(biāo)簽瞳腌,除非該方法完全只是用來做類型轉(zhuǎn)換的环鲤。
推薦:

let rgbForeground = RGBColor(cmykForeground)
  • 基于函數(shù)和方法的副作用對(duì)它們命名
    • 沒有副作用的方法讀起來應(yīng)該是一個(gè)名詞詞組憎兽,譬如:x.distance(to: y), i.successor()
    • 有副作用的方法讀起來應(yīng)該是一個(gè)命令式的動(dòng)詞短語西剥,譬如:print(x), x.sort(), x.append(y)亿汞。
    • 使用 “ed/ing” 規(guī)則對(duì)一個(gè)可變方法(mutating method)的不可變版本命名。
      一般來說疗我,可變方法都會(huì)有一個(gè)對(duì)應(yīng)的不可變版本,該方法會(huì)返回一個(gè)和接受值相同或者相似類型的值旧找。
      • 傾向于用過去分詞對(duì)不可變版本命名(一般是加 “ed”):
/// Reverses `self` in-place.
mutating func reverse()
/// Returns a reversed copy of `self`.
func reversed() -> Self
...
x.reverse()
let y = x.reversed()
- 當(dāng)動(dòng)詞后面跟了個(gè)名詞的時(shí)候麦牺,用過去分詞就不符合語法規(guī)范了鞭缭,這時(shí)候可以用動(dòng)詞的現(xiàn)在分詞對(duì)不可變版本命名魏颓,也就是加上 “ing”:
/// Strips all the newlines from \`self\`
mutating func stripNewlines()
/// Returns a copy of \`self\` with all the newlines stripped.
func strippingNewlines() -> String
...
s.stripNewlines()
let oneLine = t.strippingNewlines()
  • 調(diào)用返回值為布爾型的不可變方法和屬性的時(shí)候讀起來應(yīng)該是調(diào)用者的斷言(assertions)甸饱,譬如:x.isEmpty, line1.intersects(line2)

  • 用來描述是什么的協(xié)議讀起來應(yīng)該是個(gè)名詞柜候。**(譬如:Collection)。

  • 用來描述能做什么的協(xié)議應(yīng)該加上 able鹦肿、ible 或者 ing 進(jìn)行命名**(譬如:Equatable, ProgressReporting)辅柴。

  • 其它類型屬性涣旨、變量約束的命名都應(yīng)該用名詞股冗。

待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烹棉,隨后出現(xiàn)的幾起案子怯疤,更是在濱河造成了極大的恐慌,老刑警劉巖集峦,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塔淤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡高蜂,警方通過查閱死者的電腦和手機(jī)妨马,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門杀赢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湘纵,“玉大人,你說我怎么就攤上這事梧喷。” “怎么了铺敌?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵偿凭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我弯囊,道長(zhǎng),這世上最難降的妖魔是什么斤斧? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任霎烙,我火速辦了婚禮,結(jié)果婚禮上游昼,老公的妹妹穿的比我還像新娘盗忱。我一直安慰自己羊赵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布闲昭。 她就那樣靜靜地躺著靡挥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跋破。 梳的紋絲不亂的頭發(fā)上瓶蝴,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天舷手,我揣著相機(jī)與錄音劲绪,去河邊找鬼。 笑死贾富,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的汗捡。 我是一名探鬼主播汇鞭,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼台囱!你這毒婦竟也來了读整?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤强品,失蹤者是張志新(化名)和其女友劉穎屈糊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夫晌,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昧诱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年盏档,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凶掰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懦窘。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖畅涂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毅戈,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布赘理,位于F島的核電站扇单,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蜘澜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一瞪醋、第九天 我趴在偏房一處隱蔽的房頂上張望装诡。 院中可真熱鬧,春花似錦宾巍、人聲如沸渔伯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鲜侥,卻和暖如春诸典,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背狐粱。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工肌蜻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒋搜。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓豆挽,卻偏偏與公主長(zhǎng)得像育谬,于是被迫代替她去往敵國(guó)和親帮哈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容