Swift API 设计指南(上)

868 阅读2分钟
原文链接: www.jianshu.com
  • 包含所有需要的单词,以避免人们在阅读调用处的代码时感到困惑。
    譬如,有一个方法,要在集合(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?
  • 删除不需要的单词。名字中的每个单词都应该在调用处传达出重点信息。
    更多的单词或许能澄清意图和消除歧义,但是那些读者已经知道的冗余信息都可以删掉,尤其是那些仅仅重复了类型信息的单词。
    不推荐:

    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
    }
  • 为弱类型信息的参数添加补充信息以表明参数的角色
    当参数类型是NSObject、Any、 AnyObject或者像Int、String这样的基本类型的时候,调用处的类型信息和上下文环境可能不能完全表明函数的意图。如下这个例子,它的声明可能是明确的,但在调用的地方就显得意图不明了。
    不推荐

    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