本文翻译自 Making your iOS application easy to read with these simple steps.
优秀的程序员会用尽可能简单的方式来解释他们的代码,即使是物理学家都可以用一张白纸和一只铅笔来解释虫洞,我们又何尝不可?
我会尽可能让代码写地简单、易读,包括选择合适的变量名、使用编码规范(code conventions)等等,但还是缺了点东西,理解代码不应该是去理解“如何”实现的,而是要理解想要“达成”什么。
甚至可以说要让读代码像读小说一样,而不是一大堆代码。
下面讨论三大主题:
问题
阅读其他人的代码可能会非常折磨,如果不提供合适的上下文,我们会迷失在寻找某个函数或属性的意义中。
建议
不论是二进制语言、低级语言还是高级语言,语法都在变得越来越友好,以便吸引更多开发者。而随着语法变得更接近英语,我们的代码也应该简明扼要、不言自明。
结果
写出良好的代码,读起来像小说一样,容易阅读和理解(即使没有给出上下文)。
函数命名
正确的方式是:
我们写函数时都会假设阅读它的人拥有足够的上下文,能够理解函数想实现什么。用模糊的含义来命名函数,例如“handleRedView()”会引起很多问题,”RedView”是啥?这个函数主要是想干啥?
所以在某些情况下函数的用途会很模糊,如果没有提供足够的上下文,阅读起来会非常困难
我们可以把函数的用途分为四类:
1. 通知函数
通常会触发路由/管理函数,例子如下:
- delegate.dataHasUpdated()
- func dataHasUpdated()
- {
- //通知某事的发生
- }
- // 通知函数
- override func engineStarted()
- {
- super.engineStarted()
- handleCarStarted()
- }
回调函数,通知某事已经/即将发生,并给机会进行响应。
大部分情况下用于被代理(delegate)触发的操作,或是通知(notification)处理函数。
2. 管理函数
用于联合多个函数以实现更高级的用途,不需要依赖参数,block 中的所有代码都会执行。
- // 管理函数
- func handleCarStarted()
- {
- turnLights(on: true)
- turnAC(on: true)
- }
上面的函数包含所有需要的信息,汽车启动时执行这些函数,此时我们不关注这是“如何”实现的,而是关注它做了“什么”。
3. 路由函数
用于联合多个函数以实现更高级的用途,需要依赖一些参数,只在我们想执行的时候才执行。
- // 路由函数
- private func turnLights(on shouldTurnLightsOn: Bool)
- {
- if shouldTurnLightsOn
- {
- turnExteriorLightsOn()
- checkForBurnedBulbs()
- }
- else { turnExteriorLightsOff() }
- 如果 if 语句只执行一件事,我喜欢把 "if" "else" 和执行语句写在同一行,这样代码读起来会更流畅。
如果 if 语句只执行一件事,我喜欢把 "if" "else" 和执行语句写在同一行,这样代码读起来会更流畅。
路由函数通常同于指向执行函数,但在某些情况下,如果逻辑代码不超过一行,也可以包含自己的逻辑。
4. 执行函数
函数名字的具体实现。
- // 执行函数
- private func turnExteriorLightsOn()
- {
- leftFrontLight.isOn = true
- rightFrontLight.isOn = true
- leftBackLight.isOn = true
- rightBackLight.isOn = true
- }
- private func checkForBurnedBulbs()
- {
- for lightBulb in bulbs where !lightBulb.isUseable
- {
- Dashboard.(errorType: .lights)
- break
- }
- }
这样就能写出一个干净、简短的类,可读性强,容易维护。
* 谨记一项原则,每个函数都应该只有一个责任。
避免在函数名称里使用”and“: playAndMinimize() loadAndPlay() 这个坏习惯会打破单一责任原则,写出能够适应两种情况的代码。
避免在函数名称里进行猜测: moveRedViewIfNeeded() 上面的例子会导致后面的程序员必须深入此函数,才能理解触发移动 Red View 的时机,这样不够清晰。
注
谈及代码可读性,我首先会想到编码规范(code convention),它们被普遍接受、应用广泛,但使用编码规范并不一定会提升代码质量,虽然有跨应用性但可读性更差。
”is“前缀应该用于布尔型变量和方法,以便解释返回值是布尔类型的。#编码规范
既然”if“语句总是用于布尔值,那还有必要给每个布尔属性都加上”is“吗?为什么苹果要把 Swift 语法从 view.hidden 改成 view.isHidden?我只能想到一种答案……因为**“if view.isHidden”**看起来更自然。
尝试以以下原则使用“is”前缀:
如果类的某个布尔属性/方法用于该类(公开)的实例,就有正当理由使用“is”前缀。
- public var isHidden: Bool
- {
- return alpha == 0.0
- }
- if containerView.isHidden
如果布尔属性在类内部(私有)使用,前缀就是多余的。
- private var positionedVerticaly: Bool
- {
- return view.frame.width/2 == centerX
- }
- if positionedVerticaly
- if positionedVerticaly && positionedHorizontally
- VS
- if isPositionedVerticaly
- if isPositionedVerticaly && isPositionedHorizontally
如果布尔型属性/方法同时被私有和公开使用,那么应该用计算属性来返回该私有属性值。
- public var isPositionedVerticaly: Bool
- {
- return positionedVerticaly
- }
- if containerView.isPositionedVerticaly
虽然用私有 set 并公开使用该属性也是可以的,但上面这种方式可以实现封装(encapsulation)。
封装用于隐藏类中结构化数据对象的值或状态,防止未授权方直接访问。
那如果不是自己直接处理的布尔型,如果如何命名呢?
- private var playerIsPlaying: Bool
- private var gridConstraintIsEnabled()
“is” 需要指向某个东西:view.isHidden, “is”指向 view。上面的例子里使用了此原则,playerIsPlaying,“is”指向 player。
谨记:开发者通常会在阅读属性声明之前先阅读函数内部的代码,尝试搞明白这些属性的用途。
- /if playerIsPlaying { }/ 对比 /if isPlayerIsPlaying {}/
哪个更自然?我想你已经有答案了。
本文转载自微信公众号「码农田小齐」,作者小齐本齐 。转载本文请联系码农田小齐...
双十一刚过去不久,双十二剁手季又已经到来。在2020年最后一个购物节,想必很多...
总而言之,集中式电力网络会按照某些规则工作,以确保其在一天中的任何时候都不...
致力于节能减排的行业人士可能会对联合国在去年12月发布的《2020年碳排放差距调...
据外媒,在旧金山动物保护组织 SPCA 的合作下,一家名为 Companion Labs 推出了...
提起如今IT圈最火的技术,就不得不提起人工智能了。 人工智能是本世纪引领世界未...
1月3日,据消息人士透露, 中国银联与腾讯旗下财付通公司近日已就条码支付互联互...
虽然计算机、机器可以不眠不休的工作,但近期科学家发现,有些AI跟人类一样,也...
在渗透中,当我们拿下一台服务器作为跳板机进一步进行内网渗透时,往往需要通过...
随着远程工作重新定义了传统的工作场所,IT团队需要实施新的协作计划,以维持业...