这个世界上所有事物都是相对的,只有「相对」本身是绝对的
接盘侠日记

最屌文件命名

曾经有一个合作的设计师出了一套新版本界面设计稿,她新建了一个文件夹打包上传到内部 GitLab repo ,我 pull 下来看到这个文件夹

image

这是我目前看到过最屌的命名,于是我想下一个版本的设计稿文件夹她会起什么名字:「目前最最新的稿子」?「比目前最新的稿子更新的稿子」?「未来最新的稿子」?无论选择这其中的哪一种方案都会有一个共同特点,那就是下一次的起名都必须依赖于这一次的名字,以用来表明这个文件夹更新,而这种依赖是递增不可绕过的,比如说选用第一种方案那么到下下下下个版本就是「目前最最最最最最新的稿子」,以此类推这显然是一个灾难。当然实际中我们的设计师并不会这样,更常见的情况是下一次她的命名直接进入另一个次元,「稿子新」比「目前最新的稿子」更新,这样就可以区分出哪个更新,现在的使用者是清楚了可是接盘侠呢,果然光荣与伟大并不是那么得容易担当。

绝对和相对

当需要设定某种唯一标识的时候,绝对的概念能够更明确的定位。当需要描述一种关系的时候,相对的表述方法更加适合。

相对和绝对路径是一个直观的例子。

1
2
/Users/fengweizhou/Documents/dopcnblog/source/_posts/absolute-vs-relativity.md
source/_posts/absolute-vs-relativity.md

绝对路径则是由根目录开始一层一层明确写出到达目标文件的完整路径。相对路径可以直接用文件名引入同一个文件夹中同级的文件,用 ../ 代表上一级目录。显然两种不同用法在不同的环境中各有优势,在决定使用哪一种用法之前能够明确区分两种方法的不同很重要。

其他绝对和相对的例子

绝对和相对的界面构建

界面布局的相对和绝对。在这个领域「相对」有压倒性的优势,在浏览器端响应式网页应该是如今网站建设的标配,除了流量特别大的网站可能会为了特别定制将移动端网页分出去。使用 CSS 进行网页布局可以分为三个阶段:手写 CSS -> 用 Sass 写 CSS -> 用 Sass 写 Flexbox。在阶段一是使用「绝对」的方式写 CSS,这样依然可以写出「相对」的布局


HTML 文档在布局时天生就是「相对」的,父元素子元素间相对,同级元素之间的相对。但是这种相对布局的规则是由开发者提前「绝对」写死的。进入阶段二,这个阶段 Sass 为 CSS 引入了编程特性,程序员可以像用编程语言一样写 CSS ,于是写 CSS 的方式变为「相对」。

进入阶段三 Flexbox 模型将布局规则抽象为指令,开发者只要列出应该怎样布局剩下的大部分都交给了浏览器去计算,开发者写出的规则也变成「相对」的了。

iOS 设备上的界面构建技术稍落后于前端不过遵循了同样的路径。从只有一种屏幕大小的时代手写固定的 CGRect,到在 Size Class 中建立 AutoLayout 约束规则,这样编写出的约束规则依然是绝对的,最后再到使用 UIStackView 布局减少了许多需要手动设置 AutoLayout 的步骤。对比最初的手写固定 CGRect 越来越相对。

页面转换路径的相对绝对

我之前合作的产品经理同时负责产品的网页端和客户端,所以可能就是因为这个原因在客户端的设计过程中页面转换逻辑几乎是按照网页端的来,网页端的页面转换是链接跳转,类似于利用指针访问可以任意跳转,相比之下客户端的页面转换逻辑大多时候是后进先出的栈结构。再加上以前页面转换逻辑的写法相对绝对

1
2
let vc = SomeViewController()
navigationController?.pushViewController(vc, animated: true)

没少为设计方案来回讨论。后来为了为了尽可能适应这种网页端转换逻辑同时也扩大转换的自由度增加了URLTransformer 层,简单的原型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private protocol URLTransFormer {
func canTransform(url: NSURL) -> Bool
func transform(url: NSURL) -> UIViewController
}
//======================
//transformer list start
private struct Page1Transformer: URLTransFormer {
func canTransform(url: NSURL) -> Bool {
if url.scheme == "mydomain" {
return true
}
return false
}
func transform(url: NSURL) -> UIViewController {
return SomeViewController(url: url)
}
}
//tranformer list end
//===================
struct URLTransformer {
private let transformerList = [Page1Transformer()]
func transform(url: NSURL) -> UIViewController? {
for transformer in transformerList {
if transformer.canTransform(url) {
return transformer.transform(url)
}
}
return nil
}
}

这样页面转换逻辑就变成相对的,客户端同一个按钮可以根据服务器返回的链接形式打开不同的页面。这里服务器返回的链接一定要是绝对的完整路径,因为这个链接是在做一个唯一性的标识。

描述一种相对的关系

程序运行逻辑里有许多部分是要描述一种关系,例如注册页面在用户名密码通过格式验证之后提交按钮才可用

1
2
3
4
5
6
7
8
9
10
11
12
13
import ReactiveUIKit
import ReactiveKit
……
combineLatest(usernameField.rText, passwordField.rText).map { username, password in
guard let name = username, let pass = password else {
return false
}
return name.characters.count > 7 && pass.characters.count > 7
}.bindTo(submitButton.rEnabled)
……

简单地使用用户名和密码都必须大于7位做验证条件,这个验证的 true false 和按钮 enable 状态的true false 之间是一种对应关系,如果用常用的赋值方法来每次改动这个值,可以想象条件分支会非常多,相对而言会不容易维护。这里直接描述对应关系使用的是第三方库 ReactiveKit,除了这个还有其他一些可以实现类似功能的库,不过达到的效果是类似的。