iOS吃饭技巧程序员IOS开发

Swift API 设计指南(上)

2016-03-20  本文已影响881人  Sheepy

本文翻译自苹果官方文档:Swift API Design Guidelines,如有错漏,欢迎指出。

基本准则

命名

促使能被明确调用

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?
public mutating func removeElement(member: Element) -> Element?
allViews.removeElement(cancelButton)

上述情况下,Element在调用处没有提供任何要点信息,如下 API 会更好。
推荐

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

个别情况下,重复类型信息对于消除歧义是必要的,但一般来说,用一个表明参数角色(role)而不是类型的词,会更好一些。详情请参看下一条。
基于变量、参数、关联类型的角色来对它们进行命名,而不是基于它们的类型。
不推荐

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

像这样重申一遍类型名并不能最大程度提升明确性和表现力。相反,我们应该尽量选用那些表明实体角色的名字。
推荐

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

如果某个关联类型和它的协议联系非常紧密,以至于它的协议名就是它的角色名,那就给关联类型的名字加上Type避免冲突:

protocol Sequence {
    associatedtype IteratorType : Iterator
}
func add(observer: NSObject, for keyPath: String)
grid.add(self, for: graphics) // vague

为了恢复明确性,在每个弱类型参数前加一个名词用来描述它的角色。
推荐

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

为能被流畅调用而努力

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()

为了流畅性,可以把调用时非重点的参数放到第一或者第二个参数之后。

AudioUnit.instantiate(
    with: description, 
    options: [.inProcess], completionHandler: stopProgressBar)
let foreground = Color(red: 32, green: 64, blue: 128)
let newPart = factory.makeWidget(gears: 42, spindles: 14)

而下面这段代码的 API 作者企图用 first argument 创建符合英语语法的顺畅 API:
不推荐

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

事实上,本指南包含了参数标签(argument labels,译者注:应该和外部参数名一个意思吧)这样的的主题,意味着第一个参数都应该包含一个标签,除非该方法完全只是用来做类型转换的。
推荐:

let rgbForeground = RGBColor(cmykForeground)
/// Reverses `self` in-place.
mutating func reverse()
/// Returns a reversed copy of `self`.
func reversed() -> Self
...
x.reverse()
let y = x.reversed()
- 当动词后面跟了个名词的时候,用过去分词就不符合语法规范了,这时候可以用动词的现在分词对不可变版本命名,也就是加上 “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()

待续

上一篇 下一篇

猜你喜欢

热点阅读