编程

API 设计:真实世界的情况 2

449 2023-05-25 10:31:00

在前面的文章中,我们研究了一些真实存在下的 API,对其中好的和坏的进行了特别说明。本文中,我们将继续同样的事情!

› Python 的 datetime.datetime

大多数经验丰富的Python爱好者在职业生涯的某个阶段都写过这样的代码:

import datetime
now = datetime.datetime.now()
print(now)

虽然没有错,但重复的命名令人不快。

Go 在一篇关于包命名的博客文章中特别提到了这一点,我认为我不可能改进它,所以我会逐字逐句地引用它:

Avoid stutter. Since client code uses the package name as a prefix when referring to the package contents, the names for those contents need not repeat the package name. The HTTP server provided by the http package is called Server, not HTTPServer. Client code refers to this type as http.Server, so there is no ambiguity.

你可以使用 Python 的 from x import y 语法在一定程度上解决这一问题:

from datetime import datetime
now = datetime.now()
print(now)

作为一个有趣的小额外,一个朋友给我发了下面的 Ruby:

files = Dir.entries(Dir.pwd)
files.select! { |file| File.file?(file) }

› Java 的 URL.equals 方法

如果你要测试两个URL是否相等,你会怎么做?

对字符串表示进行比较是完全合理的。”https://google.com” != “https://facebook.com”。不过,这并不是 Java URL 类的原始作者采用的方式。

import java.net.URL;

public final class Main {
  public static void main(String... args) throws Exception {
    URL a = new URL("https://google.com");
    URL b = new URL("https://google.com");
    System.out.println(a.equals(b));
  }
}

是否始终为 true?

很遗憾不是。

java.net.URL.equals 通过一个长的间接链,最终为这两个URL调用 java.net.Inet.InetAddress.getByName,这将执行 DNS 查找,以检查它们是否解析为相同的IP地址。它有一个缓存,但如果你非常不走运,缓存在两次查找之间过期,那么如果该主机名使用 DNS 轮询,你可能会使得同一主机名称获得两个不同的 IP 地址,这在 2019 年很常见。

java.io.File.exists 类似,这个方法并没有完全按照它宣称的那样做。因为如此,再加上它进行阻塞网络调用,它被静态分析工具标记。

› Go 的标准库

我犹豫是否要添加这一部分,因为它相当有争议,但我确实认为这里有一个真正的问题。

Go 的标准库缺少一些我希望在标准库中找到的关键内容。使语言的实现与使用简单的需求,这一工作已经从语言实现者转移到了语言用户身上。

例如,检查 list 是否有特定的元素并不是该语言为您提供的。如果你查一下,你会发现这样的方法写起来很琐碎。虽然这是真的,但我发现自己每周都会频繁地查找和复制粘贴琐碎的方法。

在标准库中添加通用操作的努力是值得赞赏的,但往往很笨拙。要获得一个绝对值,需要强制转换成 float64 和从 float64 强制转换。对数组进行排序需要牺牲类型安全性并提供自己的交换函数。创建 big int 时,每个类型都有一个方法,您可以从中创建它们,并将该类型追加到方法名称中。

泛型和方法重载的添加,在使语言及其实现复杂化的同时,将使语言用户的生活更轻松。

› 单词 “filter”

通常情况下,你会如何定义“filter”这个词?包括我在内的大多数人都在想到过滤东西。金属中的杂质,黄金中的污垢,意大利面中的水。

那么,这会带来什么回报呢?

list(filter(lambda n: n > 5, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
If you guessed “a list containing all numbers greater than 5”, you would be correct.

我觉得这很困惑。我几乎每次使用过 filter 时都要检查它的工作方式,尽管在我为这篇文章所做的研究中,我了解到 filter 在我检查的每种语言中的工作方式都是一样的。

我喜欢 Ruby 解决这个问题的方法:

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list.select { |n| n > 5 } # [7, 8, 8, 9]
list.reject { |n| n <= 5 } # [0, 1, 2, 3, 4, 5]

select 和 reject 更为直接显而易见,他们是这些用例好的通用词。至少它们比 filterfalse 好。

› 结语

另一篇文章,另一组真实世界的 API 示例和改进建议。

我很想听听你的想法,如果你有任何你喜欢或讨厌的 API,我也很想听听!