Perfect API Design
良好的API设计规范
良好的API设计规范
Why is good Application Programming Interface (API) design so difficult? Just throw together a few classes with a few basic methods on them and call it done.
You could do this, if you don't mind other developers cursing you—and referencing your API as an anti-pattern. How can this be avoided? Aim for perfection with API design. Yes, perfection.
为什么设计良好的应用程序接口(API)总是很难设计,接口并不仅仅是定义一些类还有方法,可以使用就OK了。这样只有设计者知道如何使用,不规范的接口会被其他的程序员抱怨,或者会被作为反例来进行介绍。那么如何规避这些问题,如何设计出良好的API哪?
You could do this, if you don't mind other developers cursing you—and referencing your API as an anti-pattern. How can this be avoided? Aim for perfection with API design. Yes, perfection.
为什么设计良好的应用程序接口(API)总是很难设计,接口并不仅仅是定义一些类还有方法,可以使用就OK了。这样只有设计者知道如何使用,不规范的接口会被其他的程序员抱怨,或者会被作为反例来进行介绍。那么如何规避这些问题,如何设计出良好的API哪?
Designing an API is similar to designing a user interface. Both are entry points into an application and the first thing a user (or developer) interacts with. Picture your favorite consumer electronics device in conjunction with its competitors. Mine is my beloved TiVo digital video recorder. What makes the TiVo UI great?
设计应用程序接口(API)与设计UI界面很类似,用户通过它们与应用程序进行交互。说一下你最喜欢的电子产品以及它的竞争对手,Tivo是我最喜欢的一款数码录像机,Tivo具有非常好的接口设计:
设计应用程序接口(API)与设计UI界面很类似,用户通过它们与应用程序进行交互。说一下你最喜欢的电子产品以及它的竞争对手,Tivo是我最喜欢的一款数码录像机,Tivo具有非常好的接口设计:
- Usage is simple
- Misuse is difficult
- TiVo does one thing well
- It does not do anything unexpected
- It has never crashed in multiple years of usage
- Actions/tasks are intuitive and easy to discover
- The UI is consistent, polished and perfect, or close to it
- 用法简单
- 易理解,易上手
- 做好重要功能
- 并没有期待能做好所有事情
- 在长达几年的使用中系统不会崩溃
- 功能很直观,很容易被发现
- 统一的UI设计
The same principles hold true with API design. Let's pick on one of my favorite API anti-patterns from the COM world: IOleCommandTarget::Exec. This API is typically used to enable some kind of object, and the container that it sits in, to dispatch commands to each other.
设计应用程序接口(API)的设计也因该遵循这样的原则。让我们来看来自COM的一个例子:IOleCommandTarget::Exec。这个API通常被用来执行某个命令。
HRESULT Exec (
const GUID *pguidCmdGroup, // Pointer to command group
DWORD nCmdID, // Identifier of command to execute
DWORD nCmdExecOpt, // Options for executing the command
VARIANTARG *pvaIn, // Pointer to input arguments
VARIANTARG *pvaOut // Pointer to command output
);
设计应用程序接口(API)的设计也因该遵循这样的原则。让我们来看来自COM的一个例子:IOleCommandTarget::Exec。这个API通常被用来执行某个命令。
An example implementation is a COM control that communicates with its container to modify menu items and toolbars. This API is a slightly stronger-typed version of a command interface that takes in a generic string dictating what the implementation is supposed to do.
一个通常的例子就是在COM控件中如何通过其容器来修改菜单或者工具栏。这个API通过一个字符串来确定命令的,没有通过强类型判别。
Let's analyze this API according to the principles just discussed.
最后根据我们上面的原则来分析这个API.
最后根据我们上面的原则来分析这个API.
Is the API Intuitive and Discoverable? Is this the first place a developer would look to modify a toolbar? Likely it's not, unless you have the tribal knowledge about this API. Even a search of MSDN documentation would end in frustration.
这个API是非常直观和容易被发现的吗?当程序员想去修改工具栏的时候会第一个看到这个地方吗?如果不是的话,除非关于这个API有一个部落知识库,否则即使通过MSDN可以搜索得到也是非常失败的。
这个API是非常直观和容易被发现的吗?当程序员想去修改工具栏的时候会第一个看到这个地方吗?如果不是的话,除非关于这个API有一个部落知识库,否则即使通过MSDN可以搜索得到也是非常失败的。
Is It Simple? The API takes five parameters that are generically named. Documentation is required to thoroughly understand the usage of each parameter. A simple API exhibits intention and clarity just by the name of the object (for context) and the name of the method.
简单吗?这个API需要传入五个参数,文档撰写者需要非常清楚每一个参数的用法。一个简单的API只需要通过其上下文对象以及它的名字就可以阐述其主要功能,好的API应该是自描述的。
简单吗?这个API需要传入五个参数,文档撰写者需要非常清楚每一个参数的用法。一个简单的API只需要通过其上下文对象以及它的名字就可以阐述其主要功能,好的API应该是自描述的。
Is the API Strongly Typed and Difficult to Misuse? No. The variants are not strongly typed and the execution options are not enumerations, but generic DWORDs. Strongly typed parameters help find errors at compile time instead of at run time.
这个API是强类型的吗,容易传错参数吗?显然不是,variants不是强类型的,执行参数也没有定义为枚举类型而是整数。强类型的参数将会帮助用户在编译时发现错误,而不是运行时。
这个API是强类型的吗,容易传错参数吗?显然不是,variants不是强类型的,执行参数也没有定义为枚举类型而是整数。强类型的参数将会帮助用户在编译时发现错误,而不是运行时。
Is the API Cohesive? An implementation of this function typically has many responsibilities, not just one. A well-named API for one specific purpose (e.g., AddToolbarButton) is much more cohesive. An implementation of the Exec method likely switches on the command ID and has different actions based on this condition.
这个API是内聚的吗?显然这个API具有多项职责,而不是单一职责。单一职责的API往往会更加的独立,命名也会更加易读例如AddToolbarButton。
这个API是内聚的吗?显然这个API具有多项职责,而不是单一职责。单一职责的API往往会更加的独立,命名也会更加易读例如AddToolbarButton。
Is the API Free of Side Effects? Hard to say, but an implementation of the API could do practically anything due to its lack of cohesion.
这个API没有任何副作用吗?很难说没有,由于缺乏内聚形这个API实际上可以做一些别的事情。
这个API没有任何副作用吗?很难说没有,由于缺乏内聚形这个API实际上可以做一些别的事情。
Is It Reliable? The famous design principle "design to interfaces" helps enforce testability on an object. This API is inherently testable, but the number of test cases to verify may be huge due to its lack of cohesion. Reliability is at risk.
这个API是可靠的吗?设计接口的一个著名的原则就是要求具有可测性,显然这个接口是可测得,但是测试用例的数量是巨大的,稳定性是一个风险点。
这个API是可靠的吗?设计接口的一个著名的原则就是要求具有可测性,显然这个接口是可测得,但是测试用例的数量是巨大的,稳定性是一个风险点。
Is It Consistent? This API does have some consistency in that it returns an HRESULT and uses GUIDs to identify sets of information. However, it is difficult to judge consistency without knowing how poorly designed the rest of the interfaces on an object are.
这个API与系统的其他API一致吗?看起来跟其他的API一样,返回值均为HRESULT,并且使用GUID来指定信息。然而如果对于这个对象的其他接口了解较少的话,很难做出判断。
这个API与系统的其他API一致吗?看起来跟其他的API一样,返回值均为HRESULT,并且使用GUID来指定信息。然而如果对于这个对象的其他接口了解较少的话,很难做出判断。
Anyone learning to use IOleCommandTarget::Exec likely needs instruction about its usage from a peer or a book. The best APIs are simple and usable without documentation, although documentation may still be required to understand details such as error handling.
任何一个人在使用这个接口的时候,可能都需要从同伴或者书本来了解如何使用。良好的API应当是简单的,没有文档也能正确的使用。当然如果你想了解更多例如关于接口的错误处理,文档会给你帮助。
任何一个人在使用这个接口的时候,可能都需要从同伴或者书本来了解如何使用。良好的API应当是简单的,没有文档也能正确的使用。当然如果你想了解更多例如关于接口的错误处理,文档会给你帮助。
In conclusion, your APIs and the APIs you review should be as easy to use as TiVo. A highly discoverable, strongly typed, cohesive, side-effect free, reliable, consistent, and polished API makes your developers' lives easier by increasing satisfaction and decreasing support costs. Many interfaces in the Microsoft .NET Framework are great examples. To achieve all of these design gains, aim for perfection when designing or reviewing any API design.
总的来说,你设计的API或者你审阅的API应当像 Tivo一样容易使用。具有高度自描述形的,强类型的,内聚的,无任何副作用的,可靠的,一致的API将会使得你的用户更加的Happy,他们的满意度会提升,而Support的代价会降低。Microsoft .NET Framework 的很多接口都是很好的例子。在日常工作中在设计或者审阅API的时候利用上述的原则完善你的API吧。
总的来说,你设计的API或者你审阅的API应当像 Tivo一样容易使用。具有高度自描述形的,强类型的,内聚的,无任何副作用的,可靠的,一致的API将会使得你的用户更加的Happy,他们的满意度会提升,而Support的代价会降低。Microsoft .NET Framework 的很多接口都是很好的例子。在日常工作中在设计或者审阅API的时候利用上述的原则完善你的API吧。