人性化接口

by Martin Fowler

在Ruby群体中徘徊了一段时间,我发现”人性化接口“这个词多次被提及。这个词描述了部分Ruby人对于写类接口的看法,我认为这也在两种API的设计想法之间建立起一个有趣的对比(另一种是”最小化接口“)。

人性化接口的本质在于找出人们想做什么,然后设计出接口,这样可以非常方便的实现基本功能。

它和最小化接口有一个显著的差异,那就是人性化接口倾向于做的很庞大,的确,人性化接口的设计者对于接口会变得过于庞大一点都不担心。这并不是说拥有人性化接口的类一定要象它所表现出的那么庞大。事实上,两种设计的基本功能通常是非常相似的。

这儿有个好办法可以清楚看出人性化接口和最小化接口的不同──比较Java和Ruby的list组件。Java有一个声明了25个静态方法的接口(java.util.List),而Ruby则拥有一个定义了78个方法的Array类(这是个list而不是数组)。两者在大小上的差异在于它们的线索拥有不同的风格(虽说有很多原因可以用来解释这个不同)。两者的组件都提供了相同的基本服务,然而,Ruby的数组包含了许多额外的功能。这些功能都是些相对来说比较小的东西,而它们同样也可以在Java的最小化接口上建立起来。

我们来通过一个小例子来表现这种不同:获取一个线性表的最后一个元素。
用Java我们可以这样做:
aList.get(aList.size - 1)

用Ruby我们这样做:
anArray.last

事实上更令人吃惊的是:Ruby的数据还有个first方法,于是,与其用anArray[0]表示数组首元素,还不如用anArray.first表示。
其实还有更加多变的功能。Ruby的数组有个flatten方法,可以将嵌套数组转换成为一元数组:
irb> [1,2,[3,4,[5,6],7],8].flatten
=> [1, 2, 3, 4, 5, 6, 7, 8]

这儿的要点在于,所有这些功能,不论是简单如last,还是复杂如flatten,都可以由使用者自己写,而不必增加list类的长度。最低限要求者倾向于把注意力集中在通过必要方法的最小化集合来支持这些行为,而人性化接口的设计者们则试着加入尽可能多的用的着的方法。通常这些额外的方法都被归类为辅助方法,而在最低限要求者们看来,这些功能不值得他们去补充。

现在引出了一个问题:如何决定某个方法是否应该被加入到人性化接口中去?如果你把什么都放进去,那么你将得到一个非常复杂的类。人性化接口的设计者们尝试着判别哪些最常用的功能,然后根据这些判断来设计接口,使它们更方便使用。

这些原则不仅规定了你将加入哪些方法,同时它也影响到你对于这些方法的命名。在RubyConf大会上,Tanaka Akira先生提出了为普通方法命以简短名称的意义。用的越多,你就对它们越熟悉──要记得一个简单的名字非常方便,而且也保证了代码的读写的流畅性。例如,从语法上来看,DateTime是默认的对于日期的格式化方法,而更加灵活的strptime方法能够将时间以任意形式表现出来,但这个方法并不常用。

这个命名原则与最低限要求者的方法并不冲突。确实,在Java的List接口出现时,它改变了遗留元素在方法上的获取。

Ruby的人性化接口哲学造就的另一个有趣的结果就是别名的使用。当你想获得一个线表的长度时,你应该使用length还是size?有些库使用其中一个,有些使用另一个,而Ruby的数组两种都用,互为别名,而且两者都能够生成相同的代码。Ruby人的观点是:与其告诉方法库的用户去记住应该使用哪一个,不如两者都可用。

你可能会对哪一种接口设计风格更好这个问题感到厌烦。下面,我试着概括一下人性化接口的好处(如果你想要了解最小化接口,反过来看就行了)。

大多数对象的能力取决于它的行为,而非数据。如果你只是试着提供最小化实现,那么你将为了实现一些普通的功能而针对不同用户写大量重复的代码。万一遇到象flatten这样复杂的功能,用户大概只能够自己去写递归函数了,虽然并不难,但为什么要用这么个不是问题的问题去烦扰用户呢?

即使对于像last一样简单的方法,用户都需要学习一门“方言”。为什么一个简单可读的方法,用户却不能直观的看清它们呢?好的软件总是优先考虑用户的感受,让它们绝对生活很简单。人性化接口就遵从这些原则。

人性化接口做了很多事,当然这些事用不着用户操心了。尤其是API的调用,这就像是普通工作一样简单——不论是读还是写。

其实两种风格都各有好的方面。我个人比较倾向于人性化接口,虽然我也知道它实现起来更加困难。

[b]补充:[/b]

这个问题引发过一阵轰动,也因此有了一些有趣和有用的讨论。在之前的一些时候,我可以把链接放出来并写一些评论来帮助阅读,但现在我仅仅把它们列出来而不做评论。争论大多是由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的文章,他的文章囊括了以上大多数的观点。


原文:[url]http://martinfowler.com/bliki/HumaneInterface.html[/url]

[size=medium][i]HumaneInterface[/i][/size]
by Martin Fowler
5 December 2005 Reactions

(Updated [frequently] with links at end.)

Hanging around the ruby crowd for a while, I've come across the term 'Humane Interface' quite a bit. It describes part of the rubyist attitude to writing class interfaces, I think it also sets up an interesting contrast between two schools of thought in designing APIs (the other is the MinimalInterface).

The essence of the humane interface is to find out what people want to do and design the interface so that it's really easy to do the common case.

The obvious contrast to a minimal interface is that humane interfaces tend to be much larger, and indeed humane interface designers don't worry too much about the interface being big. This isn't to say that classes with humane interfaces need be larger in terms of implementation. The fundamental functionality of the two is often quite similar.

A good way of looking at the difference between humane and minimal interfaces is to compare the list components in Java and Ruby. Java has an interface (java.util.List) which declares 25 instance methods. Ruby has an Array class (which is a list not an array) that has 78 methods. That difference in size is something of a clue of that there's a different style here (although there's more reasons for that difference). Both components offer the basic same service, but Ruby's array includes a lot of additional functionality. This functionality is all relatively small things that can be built on Java's minimal interface.

Let's take a small example to help show the difference: getting the last item on the list. To do this in Java you do:

aList.get(aList.size -1)
in Ruby you do

anArray.last
In fact it's even more startling than that: Ruby's Array has a first method too, so rather than going anArray[0] you can go anArray.first.

There's larger elements of functionality as well. Ruby's Array has a flatten method that takes nested arrays and turns them into a single level.

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

The point here is all of this functionality, whether as simple as last or as complex as flatten, can be written by clients themselves without increasing the size of the list class. Minimalists tend to focus on the minimal set of necessary methods to support these behaviors, humane designers try to add methods that are needed. Often these extra methods are referred to as convenience methods, a term that minimalists do not consider to be a complement.

This begs the question: "what's the basis for deciding what should be added to a humane interface?" If you put in everything anyone might want you'll get a very complex class. Humane interface designers try to identify what are the most common uses of a class, and design the interface to make these uses easy.

Not just does this principle inspire the methods you add, it also affects how you name them. At RubyConf, Tanaka Akira pointed out the value of preferring short names for common methods. Since these are used more often you get familiar with them - it's easy to remember brief names if you use them a lot, also it's more useful since it saves typing and reading. An example of this is the parse method on DateTime that does a default parse of common date formats and the more flexible strptime that can take any format, but you use less often.

This principle of naming isn't in conflict with the minimalist approach. Indeed when Java's List interface appeared it changed the legacy Vector's elementAt method to get.

Another interesting consequence of ruby's humane interface philosophy is the aliasing method names. When you want the length of a list, should you use length or size? Some libraries use one, some the other, Ruby's Array has both, marked as aliases so that either name calls the same code. The rubyist view being that it's easier for the library to have both than to ask the users of a library to remember which one it is.

You can get long and tiresome threads about which style of interface design is best. Here I'll try to summarize the arguments in favor of the humane interface (see MinimalInterface for the other side).

Much of an object's strength lies in its behavior, not its data. If you only try to provide the minimum, you end up with multiple clients duplicating code for common cases. In cases like flatten you end up with a bunch of people writing their own recursive functions. It's not hard, but why should they bother when it's not that rare a case?

Even for simple cases like last, readers have to learn an idiom. Why should they have to see something indirect, when a simple method reads directly? Good software thinks of the users first and makes life easy for them. Humane interfaces follow that principle.

Humane interfaces do more work so that clients don't have to. In particular the human users of the API need things so that their common tasks are easy to do - both for reading and writing.

There are good arguments on both sides. Personally I lean to the Humane Interface approach, although I do think it's harder.

Follow Ups
This one caused a bit of stir, which has led to some interesting and useful discussion. At some point I might put some narrative over the links to help you read them, until then I'll just list them. The debate was mostly triggerred by Elliotte Harold's short but robust criticism of the humane approach and James Robertson's reply (make sure you check the comments on Robertson's posts). Then came the deluge | 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 .

There's more too, I haven't spotted them all and I've only gone for those that I think add something interesting to the debate and avoid invective. There's been a tendency to over-focus on the Ruby Array vs Java List example rather than the underlying principles, but that's natural. There have been a number of good directions the discussion is going, if I get chance I'll try to develop one or two of them.

Or you could just read Joey deVilla - who includes excerpts from most of the above.

---------爱与和平的分割线---------

第一次翻译,所以...很囧[size=xx-large]orz[/size]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值