人性化界面

2005 年 12 月 5 日

在 Ruby 社区待了一段时间后,我经常听到“人性化界面”这个词。我认为它描述了 Rubyist 在编写类接口方面的态度,它也为设计 API 的两种思想流派之间建立了一个有趣的对比(另一种是 最小化界面)。

人性化界面的本质是找出人们想做什么,然后设计界面,使其能够轻松地完成常见操作。

与最小化界面形成鲜明对比的是,人性化界面往往更大,而且人性化界面设计者并不太担心界面太大。这并不是说具有人性化界面的类在实现方面需要更大。两种界面的基本功能通常非常相似。

比较 Java 和 Ruby 中的列表组件,可以很好地了解人性化界面和最小化界面之间的区别。Java 有一个接口 (java.util.List),它声明了 25 个实例方法。Ruby 有一个 Array 类(它是一个列表,而不是数组),它有 78 个方法。这种尺寸上的差异暗示着这里存在不同的风格(尽管还有更多原因导致这种差异)。这两个组件都提供了基本相同的服务,但 Ruby 的数组包含许多额外的功能。这些功能都是相对较小的东西,可以在 Java 的最小化界面上构建。

让我们举一个小例子来帮助说明这种差异:获取列表中的最后一个项目。在 Java 中,你可以这样做

aList.get(aList.size -1)

在 Ruby 中,你可以这样做

anArray.last

事实上,这比这更令人吃惊:Ruby 的 Array 也有一个 first 方法,因此你可以使用 anArray.first 而不是 anArray[0]

还有更大的功能元素。Ruby 的 Array 有一个 flatten 方法,它可以将嵌套数组转换为单层数组。

irb> [1,2,[3,4,[5,6],7],8].flatten
=> [1, 2, 3, 4, 5, 6, 7, 8]

这里的重点是所有这些功能,无论是像 last 这样简单的功能,还是像 flatten 这样复杂的功能,都可以由客户端自己编写,而不会增加列表类的尺寸。最小化主义者倾向于关注支持这些行为的最小必要方法集,人性化设计者则试图添加所需的方法。这些额外的功能通常被称为便利方法,而最小化主义者并不认为这是一个褒义词。

这引出了一个问题:“决定哪些内容应该添加到人性化界面中的依据是什么?”如果你把任何人都可能想要的东西都放进去,你就会得到一个非常复杂的类。人性化界面设计者试图确定一个类的最常见用途,并设计界面以使这些用途变得容易。

这个原则不仅启发了添加的方法,还影响了方法的命名方式。在 RubyConf 上,田中明指出,为常用方法使用短名称的价值。由于这些方法使用频率更高,因此你对它们会更加熟悉 - 如果你经常使用简短的名称,就很容易记住它们,而且它也更有用,因为它可以节省打字和阅读时间。一个例子是 DateTime 上的 parse 方法,它对常见的日期格式执行默认解析,而更灵活的 strptime 可以接受任何格式,但你使用频率较低。

这种命名原则并不与最小化方法相冲突。事实上,当 Java 的 List 接口出现时,它将遗留的 Vector 的 elementAt 方法更改为 get

Ruby 的人性化界面理念带来的另一个有趣的结果是方法名称的别名。当你想要获取列表的长度时,你应该使用 length 还是 size?一些库使用其中一个,一些库使用另一个,Ruby 的 Array 同时拥有这两个,并将其标记为别名,以便任何一个名称都调用相同的代码。Rubyist 的观点是,让库同时拥有这两个比要求库的用户记住哪个是哪个更容易。

关于哪种界面设计风格最好,你可以进行长时间且令人厌烦的讨论。在这里,我将尝试总结支持人性化界面的论点(请参阅 最小化界面,了解另一方面的论点)。

一个对象的大部分优势在于它的行为,而不是它的数据。如果你只尝试提供最少的功能,最终会导致多个客户端为常见情况重复代码。在 flatten 这样的情况下,你最终会发现很多人编写自己的递归函数。这并不难,但为什么他们要费心,当这种情况并不罕见时?

即使对于像 last 这样简单的案例,读者也必须学习一种习惯用法。为什么他们必须看到间接的东西,当一个简单的方法可以直接读取时?好的软件首先考虑用户,并为他们提供便利。人性化界面遵循这一原则。

人性化界面做了更多工作,以便客户端不必做。特别是,API 的人类用户需要一些东西,以便他们的常见任务易于执行 - 无论是阅读还是写入。

双方都有很好的论据。就我个人而言,我倾向于人性化界面方法,尽管我认为它更难。

后续

这篇文章引起了不小的轰动,引发了一些有趣且有用的讨论。在某个时候,我可能会在链接上添加一些叙述来帮助你阅读它们,在此之前,我将只列出它们。这场辩论主要是由 Elliotte Harold 对人性化方法的简短但强烈的批评以及 James Robertson 的回复(确保你查看 Robertson 帖子上的评论)引发的。然后就出现了大量评论 | Cees de Groot | Antonio Vieiro | David Hoefler | James Higgs | Peter Williams | Cedric Beust | John D. Mitchell | Stuart Roebuck | Elliotte Harold (2) | Jon Tirsen | Hitesh Jasani | Blaine Buxton | Ramnivas Laddad | Anders Noras | James Robertson (2) | Kieth Ray | James Robertson (3) | Elliotte Harold (3) | Charles Miller | Rob Lally | Bernard Notarianni | David Crow | Jim Weirich | Jim Weirich (2) | Ian Bicking | Brian Foote | Justin Gehtland | Tom Moertel | Antonio Vieiro (2) | Kris Wehner | The Server Side | Ravi Mohan | Danny Lagrouw | Piers Cawley | Peter Williams | Florian Frank | Chris Siebenmann

还有更多,我还没有找到所有,我只选择了那些我认为对辩论有所贡献并避免了攻击性的内容。人们倾向于过度关注 Ruby Array 与 Java List 的例子,而不是底层原则,但这很自然。讨论正在朝着许多好的方向发展,如果有机会,我会尝试发展其中一两个方向。

或者你可以直接阅读 Joey deVilla - 他包含了上面大多数文章的摘录。