Swift 设计指南之 编程规范
基本准则
- 用法一目了然是你设计时最重要的目的。
方法和属性这样的实体只声明一次,却会被重复调用。因此你在设计 API 时应尽可能使其简单明了。当评估某个设计时,只阅读声明往往是不够的,有时还需要检查它的使用样例,才能确保其在上下文中足够清晰。
一目了然比简洁更重要。 尽管 Swift 代码可以非常简明,但是使用少量的字符使得代码变得简短并不是我们的目的。简洁的 Swift 代码,会成为强类型系统副作用,而同时也是自然地降低版面的重要特点。
给每个声明编写文档注释。编写文档会对你的设计产生深远的影响进而增加你的见解,所以不要闲置它们。
⚠️如果你不能简单的描述 API 功能,那么你很有可能设计错了 API。
- 使用 Swift 形式地 Markdown —— Markup。
- 从描述实体行为声明的摘要开始。通常,API 应该能够通过它的声明和摘要完全理解。
/// 返回 `self` 的 "view",其中包含顺序相反的相同元素。 |
专注于摘要;这是最重要的部分。很多杰出的文档其实什么都没有,但是它们有很好的注释摘要。
如果可以,使用单一的语句片段并用句号结尾。不要写一个复杂的句子。
要描述一个函数或方法会干什么,以及会返回什么,省略那些没有意义的以及返回
Void
的说明。
/// 在 `self` 的起始处插入 `newHead` 。 |
注意:像
popFirst
等少数情况,摘要应该被多条语句按照不同的情况分成不同的片段。
- 关于描述下标访问:
/// 访问第 `index` 个元素。 |
- 关于描述创建构造器:
/// 创建一个包含 `n` 个 `x` 的实例。 |
- 对于其他声明,要描述声明的实体到底是什么。
/// 支持任意位置高效插入删除元素的列表 |
- 自由延伸一个或多个段落以及零散的条目。但是段落应该使用空行进行分割,并使用完整的句子进行描述。
/// Writes the textual representation of each ← 摘要 |
使用识别的符号文档标记(markup)元素,在适当的情况下将信息加到摘要之外。
熟知并使用符号命令语法识别零散的条目。像 Xcode 等主流开发工具为零散的条目提供了特殊的处理,其关键词如下:
命名
明确用法
- 在名字中包含所有需要的单词从而避免阅读代码的人陷入困惑。
例:考虑一个移除集合中移除指定位置的元素的方法。
✅ |
如果我们省略方法名中的 at
,它可能暗示读者该方法会进行搜索并删除集合中等于 x
的元素,而不是用 x
来指示元素在集合中的位置并删除这个元素。
⛔️ |
- 删除不需要的单词。名字中的每个单词都应在使用处表明主要信息。
更多的单词或许可以明确意图并消除歧义,但是那些众所周知的冗余信息都可以删掉。尤其是那些仅仅重复类型信息的单词。
⛔️ |
对于这种情况,Element
在调用处没有提供任何要点信息,如下 API 会更好。
✅ |
个别情况下,重复类型信息对于消除歧义是必要的,但一般来说,用一个词来表明参数作用而不是类型的词会更好一些,详见下一条。
- 变量、参数、关联类型依据作用对其进行命名,而不是基于它们的类型。
⛔️ |
重申一遍类型名其实并不能提升明确性和表现力。相反,你应该尽量选用那些表明实体作用的名字。
✅ |
如果某个关联类型和它的协议联系非常紧密,导致它的协议名就是它自身的名字,那就给关联类型的名字加上Type
避免冲突:
protocol Sequence { |
- 为弱类型信息的参数补充信息以表明参数的作用。
当参数类型是NSObject
、Any
、 AnyObject
或者像Int
、String
这样的基本类型的时候,调用处的类型信息和上下文环境可能无法完全表明函数意图。在下面的例子中,它的声明可能明确,但在调用的地方意图不明。
⛔️ |
为了恢复明确性,在每个弱类型参数前加一个名词用来描述它的角色。
✅ |
为使用舒适而努力
- 尽量使方法或函数名在调用的时,符合英语语法规范。
✅ |
⛔️ |
如果不影响方法要表达的含义,那可以简化第一个或者前两个参数,这样使用起来更加流畅。
AudioUnit.instantiate( |
将工厂方法的命名以
make
开头,如:x.makeIterator()
。构造器和工厂方法调用时应由一个不包含第一实参的短语构成,如:x.makeWidget(cogCount: 47)。
举个例子,如下的调用短语都不包含第一实参。
✅ |
而下面这段代码,API 作者试图用第一实参创建符语法连续性的API:
⛔️ |
事实上,这条规则以及下面的参数命名规则都做了这样的处理,这意味着第一实参都会包含一个标签,除非该方法只用来作无损类型转换。
let rgbForeground = RGBColor(cmykForeground) |
根据函数和方法的副作用进行命名
- 没有副作用的方法读起来应该是一个名词词组,如:
x.distance(to: y)
,i.successor()
。 - 有副作用的方法读起来应该是一个命令式的动词短语,如:
print(x)
,x.sort()
,x.append(y)
。 - 始终给一对可变方法和不可变方法命名。一个可变方法通常会有一个与之对应的不可变方法,但是不可变方法会返回一个新的值而不是更新现有的实例。
- 当操作被一个名词描述时,给可变方法使用
ed
或ing
前缀来给动词添加祈使语气。
- 当操作被一个名词描述时,给可变方法使用
- 没有副作用的方法读起来应该是一个名词词组,如:
Mutating | Nonmutating |
---|---|
x.sort() |
z = x.sorted() |
x.append(y) |
z = x.appending(y) |
- 尽量使用动词过去分词来命名不可变方法(通常是添加 `ed`):
/// Reverses `self` in-place. |
- 当添加`ed`时由于动词有一个直接对象而导致不符合语法时,命名不可变方法通常会使用动词的现在分词,即添加`ing`:
/// Strips all the newlines from \`self\` |
- 当操作被一个名词描述时,可给不可变方法使用名词并添加
form
前缀。
Nonmutating | Mutating
——————–|——————x = y.union(z)
| y.formUnion(z)
j = c.successor(i)
| c.formSuccessor(&i)
当使用不可变方法时,布尔型方法和属性读起来应该像是断言,如:
x.isEmpty
,line1.intersects(line2)
。用来描述是什么的协议读起来应该是个名词。(如:
Collection
)。用来描述能做什么的协议应该加上 able、ible 或 ing (如:
Equatable
,ProgressReporting
)。其它类型、属性、变量和约束的命名都应该用名词。
合理使用术语
Term of Art(术语),名词 : 一个词或短语,在特定的领域或行业中有更精确专业的意义。
如果一个常用的词可以清楚地表达意图,就不要使用晦涩难懂的术语。用 “skin” 就可以达到你的目的的话,就别用 “epidermis”了。术语是一种非常必要的交流工具,但是应该用在其他常用语无法表达关键信息的时候。
如果你确实要使用术语,确保它已被明确定义。
使用术语的唯一理由是,用其他常用词会表意不清或造成歧义。因此,API 应该严格依据某个术语被广泛接受的释义来进行命名。
- 不要让专家惊讶:如果我们重新定义了某个术语,那会使熟知它的人感到惊讶甚至是愤怒。
- 不要让初学者迷惑:初学某个术语的人通常都会上网搜索它的传统释义。
- 避免缩写。缩写,特别是不标准的缩写,已经算是一种术语了,因为要理解它的意思必须正确地把它翻译成完整版本才行。
所有缩写必须能轻易在互联网中搜索到它的意思。
- 有例可循。不要为一个新人去优化术语而不遵守现有的规范。
将一个线性的数据结构命名为 Array
比一些更简单的词要好,如 List
。尽管 List
对新手来说更易于理解。因为Array(数组) 在现代计算机体系中是个非常基础的概念,每个程序员都已经知道或者能够很快地学会它。总之,请使用那些为程序员所熟知的术语,这样当人们搜索和询问时就能得到回应。
在一些特定的编程领域,譬如数学运算方面,广为人知的sin(x)
就比解释性的短语verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)
要好得多。注意,在这种情况下,“有例可循”的优先级大于指南中的“不要使用缩写”,哪怕完整的词是sine
。毕竟sin(x)
已经被程序员们用了几十年了,更被数学家们用了好几个世纪了。
约定
一般约定
标注那些复杂度不是 O(1) 的计算型属性。人们总是假定存取属性是不需要什么计算的,因为他们把属性保存作为了心智模型。
尽量使用方法和属性,而不是普通函数。普通函数仅适用于一些特定情况:
- 当没有明显的
self
:
min(x, y, z) |
- 当函数为无约束泛型:
print(x) |
- 当函数的写法是公认记法的一部分:
sin(x) |
- 遵循常规用例。类型和协议用首字母大写驼峰命名法(
UpperCamelCase
)进行命名,其它则均为首字母小写(lowerCamelCase
)驼峰命名法。
缩写通常在美式英语中会根据首字母情况统一成全大写或者全小写:
var utf8Bytes: [UTF8.CodeUnit]; |
其它首字母缩略词当作普通单词处理即可:
var radarDetector: RadarScanner; |
- 当几个方法的基本意义相同,或者它们作用在明确的范围内时,可以共享同一个基本命名。
例如,我们鼓励下面这种做法,因为这几个方法基本做了相同的事情:
✅ |
由于泛型和容器都有各自独立的范围,所以在同一个程序里这样使用也是可以的:
✅ |
然而,下面这些 index
方法有不同的语义,应该采用不同的命名:
⛔️ |
最后,你还应避免返回值重载,因为在类型推断时会产生歧义:
⛔️ |
形参
func move(from start: Point, to end: Point) |
- 选择能服务于文档编写的参数名。虽然参数名不在方法入口处显示,但它们起到了重要的解释说明。
选择能使文档通俗易懂的参数名。比如,下面这些参数名就是文档读上去很自然:
✅ |
而这些就使文档难以理解且不合语法:
⛔️ |
- 简化常见使用时利用默认参数的优点。任何有一个常用值的参数都可以使用默认参数。
通过隐藏无关信息,默认参数提高了代码可读性,例如:
⛔️ |
可以更加简洁:
✅ |
提供默认参数比使用一类方法更可取,因为它减轻了 API 使用者的认知负担。
✅ |
上面的代码可能不够简洁,但它比如下的代码简洁多了:
⛔️ |
一类方法中的每个方法都需要被分别注释和被使用者理解。决定使用哪个方法之前,使用者必须理解所有方法,并且偶尔会对它们之间的关系感到惊讶——比如,foo(bar: nil)
和 foo()
并不总表示相同的方法——从几乎相同的文档和注释中去区分这些微笑差异是很无聊的。而一个有默认参数的方法将提升编码的体验。
- 尽量把默认参数放在参数列表末尾。没有默认值的参数通常对于方法的语义来说是必要的,在方法被调用的时候提供了稳定的初始形态。
实参标签
func move(from start: Point, to end: Point) |
当不同参数不能被有效区分时,删除所有参数标签,如:
min(number1, number2)
,zip(sequence1, sequence2)
。当构造器完全只用作类型转换的时候,删除第一个参数标签,如:
Int64(someUInt32)
。
第一个参数总应该是要被转换的东西。
extension String { |
然而,在缩小转换(Narrowing Conversion)中,用标签来表明范围变窄是推荐的做法。
extension UInt32 { |
头两个参数各自相当于某个抽象的一部分的情况,是个例外:
⛔️ |
在这种情况下,参数的标签应在介词之后,进而保证抽象概念清晰。
✅ |
- 此外,如果第一个参数形式是符合语法规范的短语的一部分,删除它的标签,可在方法名后加上短语的前缀词,例如
x.addSubview(y)
。
这条指南暗示了如果第一个参数不是符合语法的短语的部分形式,那它应该有一个标签。
✅ |
注意,短语必须表达正确的意思非常重要。下面的短语是符合语法正确但单词表达了错误的意思。
⛔️ |
注意,默认参数是可以被删除的,在这种情况下它们都不是短语的一部分,所以它们总是应该有标签。
- 给其它所有参数都加上标签。
特殊说明
- 在 API 中,给闭包参量和元组成员加上标签。
这些名字有解释说明的能力,可以出现在文档注释中,并且给元组成员提供了表达渠道。
/// 确保我们至少持有并唯一引用 `requestedCapacity` 元素。 |
尽管在在闭包中使用它们,从技术上来说是实参标签,但即便它们是形参时候,你还是应该在选择这些标签并在文档中使用。
闭包在方法体中被调用时跟调用方法时是一致的,方法签名从一个不包含第一个参数的方法名开始:
allocate(byteCount: newCount * elementSize) |
- 特别注意那些不受约束的类型(如,
Any
,AnyObject
和一些不受约束的泛型参数),以防在重载时产生歧义。
举个例子,考虑如下重载:
⛔️ |
这些方法来自同一组语义,其第一个参数的类型是明确的。但是当 Element
是 Any
时,一个单独的元素和一个元素集合的类型相同。
⛔️ |
为了消除歧义,应将第二个重载方法的命名加以明确。
✅ |
注意新的命名是如何更好地匹配文档的注释的。在这种情况下,写文档这种行为其实也在提醒 API 作者自己。
Swift 设计指南之 编程规范的更多相关文章
- 规范抢先看!微信小程序的官方设计指南和建议
基于微信小程序轻快的特点,我们(微信官方)拟定了小程序界面设计指南和建议. 设计指南建立在充分尊重用户知情权与操作权的基础之上.旨在微信生态体系内,建立友好.高效.一致的用户体验,同时最大程度适应和支 ...
- 《设计模式之美》 <03>面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象 现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程.面向对象和函数式编程.面向对象这种编程风格又是这其中最主流的.现在比较流行的编程语言大部分都是面向对象编程语言.大部分项目也都是 ...
- Swift - 语言指南,来自github学习
@SwiftLanguage 更新于 2016-6-6,更新内容详见 Issue 55.往期更新回顾详见<收录周报> 这份指南汇集了 Swift 语言主流学习资源,并以开发者的视角整理编排 ...
- 《Swift开发指南》
<Swift开发指南> 基本信息 作者: 关东升 赵志荣 丛书名: 图灵原创 出版社:人民邮电出版社 ISBN:9787115366245 上架时间:2014-8-5 出版日期:20 ...
- 《Swift开发指南》国内第一本Swift图书上市了
<Swift开发指南>国内第一本Swift图书上市了 既<courseId=799262">苹果Swift编程语言开发指南>视频教程地址:courseId=79 ...
- 华为C语言编程规范
DKBA华为技术有限公司内部技术规范DKBA 2826-2011.5C语言编程规范2011年5月9日发布 2011年5月9日实施华为技术有限公司Huawei Technologies Co., Ltd ...
- 中兴软件编程规范C/C++
Q/ZX 深圳市中兴通讯股份有限公司企业标准 (设计技术标准) Q/ZX 04.302.1–2003 软件编程规范C/C++ 20 ...
- 关于《Swift开发指南》背后的那些事
时间轴(倒叙)2014年8月底在图灵出版社的大力支持下,全球第一本全面.系统.科学的,包含本人多年经验的呕心沥血之作<Swift开发指南>(配有同步视频课程和同步练习)全线重磅推出2014 ...
- iOS7 人机界面设计指南
iOS7 人机界面设计指南 苹果在WWDC 2013大会上发布了iOS 7,新系统一改5年来的拟物路线,在乔纳森•艾维的主导下,加入了更多的“扁平化”和“极简”现代设计元素. iOS7系统界面 ...
随机推荐
- 快乐的JS正则表达式(二)
在上一篇中介绍了一个test方法,在本文中将使用另外一个,exec方法可以找到匹配的结果并且返回结果以及位置.exec("正则"): 简单测试: var str = "{ ...
- ECMASCRIPT 6中字符串的新特性
本文将覆盖在ECMAScript 6 (ES6)中,字符串的新特性. Unicode 码位(code point)转义 Unicode字符码位的长度是21位[2].而JavaScript的字符串,是1 ...
- [OpenCV] Image Processing - Image Elementary Knowledge
"没有坚实的理论基础,实践只会浅尝于表面." 这是两本打基础的书,没系统学过的话,怎么好意思说自己会CV. 该领域,兴军亮 这个名字屡次出现,看来是计算机视觉领域国内的年轻才俊,向 ...
- 【Java】一次SpringMVC+ Mybatis 配置多数据源经历
需求 现在在维护的是学校的一款信息服务APP的后台,最近要开发一些新功能,其中一个就是加入学校电影院的在线购票.在线购票实际上已经有一套系统了,但是是外包给别人开发的,我们拿不到代码只能拿到数据库,并 ...
- lettuce webdriver 自动化测试---玩转BDD
行为驱动开发(BDD),依然高大上的矗立在远方,很少被人问津,一方面是BDD的思想不太容易理解,别一方面BDD的资料并不多.中文的资料就更少了. 之前增写过一篇<python BDD 框架之le ...
- 超好玩!10款神奇的字符图案 & 词汇云生成工具
在这里,我们推荐10款惊人的字符图案生成工具.词云可以定义为词频的图形表示,而字符图案发生器是一个把数据,如文字和标签在以视觉和吸引人的方式展示的简单的工具.这些生成工具具有不同的功能,其中包括不同的 ...
- 一、HTTPServer,RequestHandler,ServerHandler,Handler
1. HTTPServer,RequestHandler,ServerHandler,Handler 1.1 基本概念 HTTPServer主要是对传输控制层HTTP,TCP,S ...
- task 限制任务数量(转自msdn)
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler { // Indicates whether the current ...
- 重新想象 Windows 8 Store Apps (61) - 通信: http, oauth
[源码下载] 重新想象 Windows 8 Store Apps (61) - 通信: http, oauth 作者:webabcd 介绍重新想象 Windows 8 Store Apps 之 通信 ...
- ActiveReports 报表应用教程 (9)---交互式报表之动态排序
在 ActiveReports 中除了提供对数据源进行排序的功能之外,还提供了最终用户排序功能,最终用户可以对报表进行区域内排序和整个数据源排序,结合数据钻取.过滤等功能可以让用户更方便地分析报表数据 ...