编码时请将“单位”写入名词以突出明确 - Ruud


有一个代码可读性陷阱,一旦你意识到它就很容易避免,但这个陷阱无处不在:人们喜欢省略“单位unit”。
看看 Python、Java 和 Haskell 中的以下三个片段:

time.sleep(300)
Thread.sleep(300)
threadDelay 300

这些程序会休眠多长时间?Python 程序休眠 5 分钟,Java 程序休眠 0.3 秒,Haskell 程序休眠 0.3 毫秒。

你怎么能从代码中看出这一点呢?你不可能。
你只需要用心去体会time.sleep需要几秒钟,而threadDelay需要几微秒。
如果你经常查找,最终这些知识就会被记住,但我们怎样才能让那些以前没有遇到过time.sleep的人也能读懂代码呢?
 
选项1:把单位放在名称中
你可能喜欢这样:

def frobnicate(timeout: int) -> None:
    ...

frobnicate(300)

为了明确单位,这样写:
def frobnicate(*, timeout_seconds: int) -> None:
    # 迫使调用者使用命名的参数*后的所有参数。
    ...

frobnicate(timeout_seconds=300)

对于支持命名参数的语言来说,使用命名参数是很好的,但这并不总是一种可能性。
即使在Python中,time.sleep被定义为一个名为secs的参数,由于实现的原因,我们也不能调用sleep(secs=300)。
在这种情况下,我们可以给这个值取一个变量名来代替: 

sleep_seconds = 300
time.sleep(sleep_seconds)

(banq注:使用DDD中值对象,为单位专门建立一个值对象。)

现在,代码是不含糊的,而且无需查阅文档就可以阅读。
 
选项2:使用强类型
将单位放在名称中的另一个选择是使用比整数或浮点数更强的类型。
例如,我们可以使用一个持续时间类型:

def frobnicate(timeout: timedelta) -> None:
    ...

timeout = timedelta(seconds=300)
frobnicate(timeout)

对于一个给定的浮点数,你需要被告知其单位是什么,以便能够解释它。
如果你幸运的话,这些信息就在变量或参数名称中,但如果你不幸运的话,就只能在文档中指定--或者根本就没有指定。
但是对于一个timedelta值来说,如何解释它是没有歧义的,这是类型的一部分。这也消除了代码中的歧义。
 
作用域范围
使用强类型或将单位放在名称中的建议并不限于变量和函数参数,它适用于API、公制名称、序列化格式、配置文件、命令行标志等。虽然持续时间是最常见的情况,但这个建议也不限于此,它也适用于货币金额、长度、数据大小等。
不要这么做:

{
   "error_code": "E429",
   
"error_message": "Rate limit exceeded",
   
"retry_after": 100,
}

应该这么做:

{
   "error_code": "E429",
   
"error_message": "Rate limit exceeded",
   
"retry_after_seconds": 100,
}

不要这么做:
request_timeout = 10
应该这么做:

request_timeout = 10s
request_timeout_seconds = 10