Swift API Design Guidelines|Swift API设计指南

原文地址:https://swift.org/documentation/api-design-guidelines.html

These are draft API guidelines being developed as part of the Swift 3.0 effort. 这些 API 指南草稿将作为 Swift 3.0的成果的一部分。

1. Fundamentals 最基本的

1.1.

Clarity at the point of use is your most important goal. Code is read far more than it is written. 用法的清晰是最重要的目标。阅读代码远远比写重要。

1.2.

Clarity is more important than brevity. Although Swift code can be compact, it is anon-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate. 清晰比简洁更重要。尽管 Swift 代码可以很简洁,但是目标却不是以最可能少的单词完成尽少量的代码。Swift 代码中,简洁它是存在的,但也仅仅是强类型系统和特性自然而然产生的意外效果。

1.3.

Write a Documentation Comment for every method or property in Swift’s dialect of Markdown. Ideally, the API’s meaning can be understood from its signature and its one- or two-sentence summary: 为每个方法和属性书写注释,并符合Swift 版的Markdown语法,通过签名和一两句概要即可理解 API 的含义。

1
2
3
4
5
6
/// Returns the first index where `element` appears in `self`, 
/// or `nil` if `element` is not found.
///
/// - Complexity: O(`self.count`).
public func indexOf(element: Generator.Element) -> Index? {

Insights gained by writing documentation can have such a profound impact on your API’s design that it’s a good idea to do it early on. 通过写文档获得的洞察力,可以对你设计 API 产生巨大的影响,因此最好今早的开始做。

If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API. 如果你尝试用简单的术语来描述的你的 API 的功能时都有问题,可能你设计的错误的 API。

2. Naming 命名

2.1. Promote Clear Usage 提高清晰的使用方法

2.1.1. Include all the words needed to avoid ambiguity for a person reading code where the name is used. 包含所有必需的单词以避免歧义,当读者看到名字时。

For example, consider a method that removes the element at a given position within a collection 例如,考虑一个方法用来删除集合类的指定位置的元素

1
public mutating func removeAt(position: Index) -> Element

used as follows: 用法如下

1
empliyees.removeAt(x)

If we were to omit the word At from the method name, it could imply to the reader that the method searches for and removes an element equal to x, rather than using x to indicate the position of the element to remove. 如果省略方法名中的单词At,对于读者将意味着这个方法可以搜索并移除一个等于x的元素,而非表示使用x定位将要移除的元素的位置。

1
employees.remove(x) // unclear: are we removing x?

2.1.2. Omit Needless Words. Every word in a name should convey salient information at the use site. 删除多余的单词。名字中每一个单词都表达出使用时的主要信息。

More words may be needed to clarify intent or disambiguate meaning, but those that are redundant with information the reader already possesses should be omitted. In particular, omit words that merely repeat type information: 可能需要更多单词来明确意图或消除歧义,但是多余的信息读者可能会忽略。尤其要删除那些仅仅指明类型信息的单词。

1
public mutating func removeElement(member: Element) -> Element?  allViews.removeElement(cancelButton) 

In this case, the word Element adds nothing salient at the call site. This API would be better: 本例中,Element 这个单词在调用时并没有显著的作用。下面这个 API 可能更好。

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

Occasionally, repeating type information is necessary to avoid ambiguity, but in general it is better to use a word that describes a parameter’s role rather than its type. See the next item for details. 个别情况下,重复类型信息室必要的,以避免歧义。但通常来说,使用表示参数的作用单词要比类型信息的单词的更好。下一条项目将会详细讲解。

2.1.3. Compensate For Weak Type Information as needed to clarify a parameter’s role. 对弱类型的补充说明参数的作用描述信息。

Especially when a parameter type is NSObjectAnyAnyObject, or a fundamental type such Int or String, type information and context at the point of use may not fully convey intent. 尤其是当参数类型是NSObjectAnyAnyObject 或者最基本的类型如  Int 或 String,这时候类型信息和使用时的上下文可能不能完全表达出意图。

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

To restore clarity, precede each weakly-typed parameter with a noun describing its role: 回归到清晰,优先添加描述作用的名词来命名弱类型参数

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

2.2. Be Grammatical 遵循语法规则

2.2.1. Uses of mutating methods should read as imperative verb phrases, e.g.,x.reverse()x.sort()x.append(y). 变异的方法应该使用命令式的动词短语。

2.2.2. Uses of non-mutating methods should read as noun phrases when possible, e.g. x.distanceTo(y)i.successor(). 非变异的方法尽可能的使用名词短语。

Imperative verbs are acceptable when there is no good alternative that reads as a noun phrase: 必要的使用动词也是可接受的,当没有其他好的可以读起来像一个名字短语的选项。

1
let firstAndLast = fullName.split() // acceptable

2.2.3. When a mutating method is described by a verb, name its non-mutating counterpart according to the “ed/ing” rule, e.g. the non-mutating versions ofx.sort() and x.append(y) are x.sorted() and x.appending(y). 与变异之对应的非变异自己的变异方法通常是动词加“ed/ing”

Often, a mutating method will have a non-mutating variant returning the same, or a similar, type as the receiver. 通常,一个非变异自己的变异的方法返回值是一个相同或者相似类型。

Prefer to name the non-mutating variant using the verb’s past tense (usually appending “ed”): 偏爱用动词过去式(通常是结尾添加“ed”)命名一个非变异体

1
2
3
4
5
6
7
8
/// Reverses `self` in-place. 
mutating func reverse()

/// Returns a reversed copy of `self`.
func reversed() -> Self
...
x.reverse()
let y = x.reversed()

When adding “ed” is not grammatical because the verb has a direct object, name the non-mutating variant using the verb’s gerund form (usually appending “ing”): 当因为及物动词添加“ed”不和语法时,就使用动名词形式(通常结尾添加“ing”)来命名

1
2
3
4
5
6
7
8
9
/// 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()

2.2.4. Uses of non-mutating Boolean methods and properties should read as assertions about the receiver, e.g. x.isEmptyline1.intersects(line2). 非变异的布尔方法和属性应使用断言作为接收器。

2.2.5. Protocols that describe what something is should read as nouns (e.g. Collection). Protocols that describe a capability should be named using the suffixes able,ible, or ing (e.g. EquatableProgressReporting). 描述是什么的协议用名词。描述能力的用 able,ible, or ing 等后缀。

2.2.6. The names of other types, properties, variables, and constants should read as nouns. 其余类型,属性,变量和常量用名词。

2.3. Use Terminology Well 恰当地使用术语

Term of Art

noun - a word or phrase that has a precise, specialized meaning within a particular field or profession. 一个单词或短语,有明确的特定的含义在某一特定领域或专业。

2.3.1. Avoid obscure terms if a more common word conveys meaning just as well. Don’t say “epidermis” if “skin” will serve your purpose. Terms of art are an essential communication tool, but should only be used to capture crucial meaning that would otherwise be lost. 避免使用生僻词。

The only reason to use a technical term rather than a more common word is that it precisely expresses something that would otherwise be ambiguous or unclear. Therefore, an API should use the term strictly in accordance with its accepted meaning.

  • Don’t surprise an expert: anyone already familiar with the term will be surprised and probably angered if we appear to have invented a new meaning for it.

  • Don’t confuse a beginner: anyone trying to learn the term is likely to do a web search and find its traditional meaning.

2.3.2. Stick to the established meaning if you do use a term of art.Avoid abbreviations. Abbreviations, especially non-standard ones, are effectively terms-of-art, because understanding depends on correctly translating them into their non-abbreviated forms. 坚持使用明确的单词。避免缩写,除非很容易搜索到原意。

>The intended meaning for any abbreviation you use should be easily found by a web search. 

2.3.3. Embrace precedent: Don’t optimize terms for the total beginner at the expense of conformance to existing culture. 拥抱先例:不要为了新手,以顺应先前的文化为代价去优化术语。

译者注:#Swift3 Remove the ++ and – operators
Author: Chris Lattner https://github.com/apple/swift-evolution/blob/master/proposals/0004-remove-pre-post-inc-decrement.md

3. Conventions 约定

3.1. General Conventions 一般约定

  • Document the complexity of any computed property that is not O(1). People often assume that property access involves no significant computation, because they have stored properties as a mental model. Be sure to alert them when that assumption may be violated. 指出任何复杂度非O(1)的计算属性。因为人们通常假设属性访问并非复杂计算量。

  • Prefer methods and properties to free functions. Free functions are used only in special cases. 偏爱方法和属性,而非相对独立的函数。

  • Follow case conventions: names of types, protocols and enum cases are UpperCamelCase. Everything else is lowerCamelCase. 遵循驼峰大小写法:类型,协议和枚举用大驼峰法,其余小驼峰法。

  • Methods can share a base name when they share the same basic meaningbut operate on different types, or are in different domains. 方法可以共用一个通用名,如果均指一个基本含义时,用于不同的类型或者不同的作用域。

    For example, the following is encouraged, since the methods do essentially the same things:

    1
    2
    3
    4
    5
    6
    7
    extension Shape { 
    /// Returns true iff other is within the area of self.
    func contains(other: Point) -> Bool { ... }
    /// Returns true iff other is entirely within the area of self.
    func contains(other: Shape) -> Bool { ... }
    /// Returns true iff other is within the area of self.
    func contains(other: LineSegment) -> Bool { ... } }

3.2. Parameters

  • 3.2.1. Take advantage of defaulted arguments when it simplifies common uses. Any parameter with a single commonly-used value is a candidate for defaulting. 充分利用参数的默认值。

  • 3.2.2. Prefer to locate parameters with defaults towards the end of the parameter list. Parameters without defaults are usually more essential to the semantics of a method, and provide a stable initial pattern of use where methods are invoked. 尽量含默认值参数置于参数列表后面。

  • 3.2.3. Prefer to follow the language’s defaults for the presence of argument labels 遵循语言习惯填写外部参数标签。

    In other words, usually: 换言之

    • First parameters to methods and functions should not have required argument labels. 第一参数不必指明外部参数。

    • Other parameters to methods and functions should have required argument labels. 后面的外部参数必须填写。

    • All parameters to initializers should have required argument labels. 所有涉及初始化的参数均需外部参数。

      The above corresponds to where the language would require argument labels if each parameter was declared with the form: 如果符合上述需要外部参数标签,应该向下面这么声明
      

swift ​ identifier: Type ​

>译者注:参看 http://stackoverflow.com/questions/24815832/when-are-argument-labels-required-in-swift

There are only a few exceptions: 也有例外

  • In initializers that should be seen as “full-width type conversions,” the initial argument should be the source of the conversion, and should be unlabelled. 包含全角的类型转换的初始化应该使用原始指,而且不包含外部参数标签。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    extension String { 
    // Convert `x` into its textual representation in the given radix init(_ x: BigInt, radix: Int = 10) // Note the initial separate underscore
    }
    text = "The value is: "
    text += String(veryLargeNumber)
    text += " and in hexadecimal, it's"
    text += String(veryLargeNumber, radix: 16)
    ​ ```

    In “narrowing” type conversions, though, a label that describes the narrowing is recommended:

    ```Swift
    extension UInt32 {
    init(_ value: Int16) // widening, so no label
    init(truncating bits: UInt64)
    init(saturating value: UInt64) }
  • When all parameters are peers that can’t be usefully distinguished, none should be labelled. Well-known examples include min(number1, number2) andzip(sequence1, sequence2). 如果所有参数是平等不易区分重要性,不用加外部参数标签。

  • When the first argument is defaulted, it should have a distinct argument label. 如果第一个参数是值可空,那么就应该添加一个清晰的外表参数标签。

    1
    2
    3
    4
    5
    extension Document { 
    func close(completionHandler completion: ((Bool) -> Void)? = nil)
    }
    doc1.close()
    doc2.close(completionHandler: app.quit)

    As you can see, this practice makes calls read correctly regardless of whether the argument is passed explicitly. If instead you omit the parameter description, the call may incorrectly imply the argument is the direct object of the “sentence:”

    1
    2
    3
    4
    5
    6
    extension Document { 
    func close(completion: ((Bool) -> Void)? = nil)
    }

    doc.close(app.quit) // Closing the quit function?

      If you attach the parameter description to the function’s base name, it will “dangle” when the default is used:
      
    
    1
    2
    3
    4
    extension Document { 
    func closeWithCompletionHandler(completion: ((Bool) -> Void)? = nil)
    }
    doc.closeWithCompletionHandler() // What completion handler?

4. Special Instructions 特别说明

4.1. Take extra care with unconstrained polymorphism (e.g. AnyAnyObject, and unconstrained generic parameters) to avoid ambiguities in overload sets. 特别注意不受约束类型的多态。

For example, consider this overload set:

1
2
3
4
5
6
7
8
9
struct Array { 
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(newElement: Element)

/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func append<
S : SequenceType where S.Generator.Element == Element
>(newElements: S) }

These methods form a semantic family, and the argument types appear at first to be sharply distinct. However, when Element is Any, a single element can have the same type as a sequence of elements:

1
var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]? 

To eliminate the ambiguity, name the second overload more explicitly: 为了排除歧义,重新命名时更加明确含义。

1
2
3
4
5
6
7
8
9
struct Array { 
/// Inserts `newElement` at `self.endIndex`.
public mutating func append(newElement: Element)

/// Inserts the contents of `newElements`, in order, at
/// `self.endIndex`.
public mutating func appendContentsOf<
S : SequenceType where S.Generator.Element == Element
>(newElements: S) }

Notice how the new name better matches the documentation comment. In this case, the act of writing the documentation comment actually brought the issue to the API author’s attention.

4.2. Make documentation comments tool-friendly; they will be automatically extracted to generate richly-formatted public documentation, and they appear in Xcode, in generated interfaces, quick help, and code completion. 文档注释对工具优化,方便地自动提取富文本的文档,自动显示在 Xcode,生成界面,快速帮助,代码补全中。

Our Markdown processor gives special treatment to the following bullet list keywords: Swift 版 Markdown 会特殊处理下面列出的关键词

-Attention: -Important: -Requires:
-Author: -Invariant: -See:
-Authors: -Note: -Since:
-Bug: -Postcondition: -Throws:
-Complexity: -Precondition: -TODO:
-Copyright: -Remark: -Version:
-Date: -Remarks: -Warning:
-Experiment: -Returns:

Writing a great summary is more important than leveraging keywords. 写一个很棒的概要远比仅仅添加关键词重要的多。

You can omit separate documentation for each parameter and the return type if it wouldn’t add useful information beyond what’s already conveyed by the method signature and its summary line. For example: 除方法签名和概要信息外,如果不能添加有用的信息,就可以省略参数和返回类型的注释文档。

1
2
/// Append `newContent` to this stream. 
mutating func write(newContent: String)