`
lua
  • 浏览: 74580 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

API design tips

阅读更多

API Design Tips

"It's very easy to create a bad API and rather difficult to create a good one."
- Michi Henning, API Design Matters, Communications of the ACM Vol. 52 No. 5

First think of the consumer of the API. Think about his likely situation and the problems he's facing. He doesn't want to learn a new abstraction and he doesn't like the fact that your API is going to make him change the design of his program. Design for the benefit of the consumer, not your ease of implementation.

  • Naming is everything
    1. Put your API in a namespace that reflects its function, not your org chart or company brand1
    2. Chose names that describe what each function does and what each type contains or abstracts. Avoid "ProcessData()" disease

  • The point is to reduce complexity
    APIs don't merely implement algorithms that the consumer isn't expected to understand, they also present abstractions that reduce complexity. An FTP library doesn't give the consumer a power they didn't already have, it relieves the consumer of doing the socket programming themselves. If you're wrapping a lower-level API then please don't introduce new problems. Aim for designs that reduce complexity without contributing to it in other ways

  • Eschew unnecessary side effects
    1. Avoid overwriting the arguments
    2. If you're given a DB or socket connection, assume it's open and never close it yourself
    3. Do not open your own connections if they're not the point of the API's purpose

  • Use Inversion of Control (IoC) to handle necessary side effects
    Let the consumer give you instances of resource managers or event handlers to open auxiliary connections, write to log files, fetch lookup values, interact with the user and so-on

  • Use events for the bulk of your IoC
    Don't force the consumer to create instances of a dozen different resource managers like logging classes, connection pools, name resolution classes etc. before they can call the first function. Raise events so that the consumer can handle only what they need in their circumstance

  • Use Interfaces everywhere
    When events aren't sufficient for your IoC component, or your arguments require a strongly-typed value, then use Interface types--even if you expose concrete implementations as well, and even if you're certain that your concrete implementation is the only kind that will do. Assume your consumer knows what he's doing

  • Use Interfaces that cover everything you need, but not more and not less
    Do you need IList, or just IEnumerable? Or do you actually need IBindingList and are trying to be too modest? If you specify too much you could force the consumer to work harder than necessary, and if you specify too little you risk being passed an insufficient implementation, or make it harder for the consumer to extend your API with a compatible wrapper

  • Don't make your own gravity
    If your target platform has its own crypto primitives, graphics library, transactional memory, XML parser, or whatever--and the point of your API isn't to replace them--then don't reinvent and bind your API to your own implementations, even if you think they're better

  • return all of your results from the function call
    Avoid returning data by setting global variables or writing to files, tables, sockets, etc. Let the consumer decide where the data goes and use compound data structures if it's necessary to return a success code along with the results

  • The consumer isn't Sherlock Holmes
    If an operation didn't work, or had an unavoidable (and un-IoC-able) side effect, then don't force the consumer to discover this by groping around referenced data structures or other environmental features. Return something other than null or void. Make sure that success is also clearly defined--either return an explicit success code or make sure that the absence of any error can be reasonably construed to mean success

  • Distinguish types of failures
    Did a connection fail because of a timeout, or because the remote host refused the connection, or because the address was invalid, or the login credentials invalid, or because a bird pooped on the switch, or what? Consider exposing and returning an Enum with the types of failures your API can detect

  • Support optional parameters
    Whether by a built-in language feature or via overloads, chose sensible default values or behaviors (timeout values, for example) and give the consumer the option of providing the least you need

  • true and false are lousy arguments
    If you spot the line Filter(widgets, true) somewhere in your code, tell me quick, what does true refer to? Does it mean be case insensitive, or to ignore errors, or to strip the results of blank lines? It's better to create Enums or other data structures that let the consumer's code be expressive and clear, so next time you see it, the line reads Filter(widgets, FilterOptions.IgnoreCase) and everything is clear

  • Use consistent calling conventions
    If you ask for a filename as the first argument in one of your functions, then stick to that as a convention for any other function that needs a filename. If you arrange argument order randomly you'll force the consumer to memorize the signature for all of your functions, instead of being able to intuit them

  • Nouns should be value types, verbs should be static functions. Or: Don't Make "Manager" Classes
    Avoid creating black-boxes that the consumer has to poke and prod to life

  • Fail early
    Check what the consumer is passing to you and aim to fail before you begin using and modifying resources, especially if those resources don't support rollbacks

  • Don't be the weak link in the chain
    If you use cryptography or handle sensitive data, then have your product audited by a security professional. Use well tested, standardized crypto primitives that have been implemented by security professionals. Be careful to avoid buffer overflow vulnerabulities. Perform strict input validation. Do not invent your own ciphers, and do not implement crypto primitives on your own (unless that's the point), because even the implementation of trusted ciphers can be easily screwed up. Your consumer doesn't want an invisible hole ripped open in their software after they did everything right on their end

  • One API at a time
    Identify a single concern for your API and stick to its scope. This concern could cover a broad category (like graphics, compression, networking), and if it does be sure to break it up into focused namespaces. You probably shouldn't bundle your compression library in the same binary and namespace as your FTP library

  • One level of abstraction at a time
    You may need to build more than one layer of abstraction to get up to the interface you'd like to present to the consumer. Networking now involves stacks 7 levels tall, for example. But keep each level separate, in the sense that each level could be re-used with a different "top" or a different "bottom" each coded to the right Interface

  • Expose wrappers for value types, but accept strings anyway
    Some consumers will appreciate a wrapper that strongly types your WidgetSerialNumber, but provide overloads that will also take a string and perform implicit conversion, too. Also favor data types already available on the platform, such as TimeSpans for timeout values instead of integers

  • Always return the richest type
    Following from above: even if the consumer passed an implicitly convertible string, always return the richest type that befits your output. If it's a date or time, return a DateTime value. If it's a number, return an Int, or a Decimal, or your own wrapper type, or whatever fits precisely. Let the consumer convert it back to a string if that's what they want

  • Implement IClonable, INotifyPropertyChanged, ISerializable and other handy interfaces for your value types
    Give your types more utility for the consumer so they don't have to marshal values back and forth, just because they want a nice reactive UI or an easy way to save and restore data to disk

  • Provide flexibility and isolation for configuration
    Libraries that link to the application's binary could be configured many ways: in code, as blocks in the application's own config file, or in a separate config file. The consumer will appreciate it if you offered at least the first two. When providing the option to configure the library through a shared config file, make sure to use namespaces or similar mechanism--such as .Net's ConfigurationSection class--to keep yours separate from the rest

  • Keep your knickers to yourself
    Consumers don't need their Intellisense or other inspection tools cluttered up with all your helper classes and utility methods. Makepublic only what the consumer needs to use your API, and mark everything else as private or internal

  • Only throw exceptions for the exceptional
    Failures in I/O operations are expected for an API that deals with I/O--an attempt to read or write can fail for a variety of reasons. You should use return values to indicate failure here and not exceptions, since throwing exceptions will add unnecessary overhead for what could be a completely normal outcome from the consumer's point of view (they may be writing a dead-link checker, and be expecting lots of FileNotFound conditions). Only throw exceptions if the fault occurs in supporting code (like when your graphics library tries to load an image from disk), or something is wrong with the consumer's code (invalid arguments, unopened connections, out-of-bounds conditions, etc.)

  • Document the exceptions you throw
    Give the consumer the chance to factor exception handling into the design of their program before it's too late to change it

  • Document how to do what your consumers tend to do
    Release 1.0 of your documentation might not contain tutorials and How-Tos for everything your consumers use your API for, but listen to feedback and have a plan to improve the How-Tos chapter of your manual for subsequent releases

  • Flag deviations from the norm
    If your API is a wrapper for a low-level service, and you change the behavior (eg: setting a timeout to zero no longer means wait indefinitely), then document this clear and in bold for those consumers who already know or reference the documentation for the underlying service

  • Consider shipping your unit tests
    They will serve both as a guide to the use of your API, and a framework to reproduce and report bugs that your consumers find in the field

  • Don't ship an API you haven't used in a useful, working program
    For everything that can't be covered here, you will learn whether your API is actually practical and usable by employing it in a program that actually does something useful--not just a unit test or a proof-of-concept, but something that has a UI or interfaces with a real system. That program doesn't necessarily have to be a product that you sell and support, it can be an internal utility, too. Bonus points if the programmer doesn't even work on the API team and therefore provides a fresh perspective. 

    If you develop the API at the same time you develop the program, you will also have the chance to see and correct mistakes that add unnecessary complexity to the consumer's program, conflicts with the naming scheme of an existing API, or exposes an interface that's unintuitive and confusing. That may be better than all of the above tips combined.
1 - It's okay to use a product name as the root of the namespace.MegaWidgets.Cryptography.DecoderRing.DRCryptoServiceProvider is acceptable, for example.

Other words of wisdom

 Michi Henning's essay, referenced at the top, was the inspiration for this collection of tips. He himself was inspired to write it after experiencing the difficulties of using the socket Select() function in the .Net framework, and he passes on some excellent admonitions that bear repeating, or to whet one's appetite for more:
  • General-purpose APIs should be "policy-free;" special-purpose APIs should be "policy-rich."

  • APIs should be designed from the perspective of the caller

  • Good APIs don't pass the buck

  • A big problem with API documentation is that it is usually written after the API is implemented, and often written by the implementor
And since publishing, someone on Hacker News pointed out this article written by Trolltech, who designed the Qt GUI framework, and someone on Reddit pointed out the book Framework Design Guidelines, written by members of the .Net team at Microsoft.

分享到:
评论

相关推荐

    Productdesign.tips-crx插件

    语言:English ...存储:用于存储从ProductDesign.tips API服务器收到的最新内容信息。WebRequest:与ProductDesign.TIPS API服务器通信以收集用户的内容订阅体验。WebRequestBlocking:访问ProductDes

    restful-api-design-tips:API设计技巧和趋势评估工作指南

    经验丰富的RESTful API设计技巧 API设计技巧和趋势评估的工作指南。 :books: 最初发布在我的中型博客上。 :link: :clapping_hands: 随时在这里阅读,如果喜欢,可以鼓掌/评论。 我们都是手Craft.io的徒弟,没人...

    Selenium Testing Tools Cookbook(PACKT,2ed,2015)

    You start off by setting up the test development environment and gain tips on the advanced locater strategy and the effective use of the Selenium WebDriver API. After that, the use of design ...

    Mastering Go Web Services

    Some basic concurrency knowledge is also required., What You Will Learn, Familiarize yourself with RESTful practices and apply them in Go Acquaint yourself with the best practices for API design such...

    Android代码-知乎豆瓣网易云综合体

    模仿网易云音乐UI,用知乎和gankio 网易新闻 豆瓣电影的API来完成一个基于Material Design Rxjava Retrofit dagger2 MVP构架的项目 为了更好的学习Material Design和主流框架,于是有了该项目。 Screenshots ...

    Mastering.Go.Web.Services.178398130X

    A comprehensive tutorial with lots of tips and tricks to program and develop web services in Go Who This Book Is For If you are a web programmer with experience in developing web services and have a ...

    Tips_and_Tricks_with_DirectX_9.pdf

    Section III — Software Shaders and Shader Programming Tips 381 Software Vertex Shader Processing 383 Dean P. Macri x86 Shaders–ps_2_0 Shaders in Software 396 Nicolas Capens SoftD3D: A Software-only ...

    Selenium Testing Tools Cookbook 最新 原版

    You start off by setting up the test development environment and gain tips on the advanced locater strategy and the effective use of the Selenium WebDriver API. After that, the use of design ...

    Windows Store App Development: C# and XAML

    Following numerous carefully crafted examples, you'll learn about new Windows 8 features, the WinRT API, and .NET 4.5. Along the way, you'll pick up tips for deploying apps, including sale through ...

    Manning.Windows.Store.App.Development.2013

    Following numerous carefully crafted examples, you'll learn about new Windows 8 features, the WinRT API, and .NET 4.5. Along the way, you'll pick up tips for deploying apps, including sale through ...

    Selenium.Testing.Tools.Cookbook.2nd.Edition.178439251

    You start off by setting up the test development environment and gain tips on the advanced locater strategy and the effective use of the Selenium WebDriver API. After that, the use of design ...

    Serverless Applications with Node.js

    Each chapter is filled with exercises, examples, tips, and more to make sure you’re ready to bring what you’ve learned into your own work. about the technology The benefits of cloud-hosted ...

    Mastering EJB 3.0 4th(E)

    Published in July 2006, the best selling book Mastering EJB is now in its fourth edition and has been updated for EJB 3.0....Best practices for EJB 3.0 application design, development and testing

    Java DevelopmentDeveloping Games with the Game API

    2 -Siemens Game API ........................................................................................................................... 3 3 -Game Canvas ..........................................

    Learn Angular The Collection

    Chapter 11:Creating Uls with Angular Material Design Components Chapter 12:Developing Angular Apps without a Back End Using MockBackend Chapter 13:React vs Angular:An In-depth Comparison Book 2:Learn ...

    Mastering EJB 3.0

    - Basic and advanced concepts (such as inheritance, relationships, and so on) of Java Persistence API defined entities - Information on integrating EJB applications with the outside world via the Java...

    Packt.Kinect.for.Windows.SDK.Programming.Guide

    Step by step installation guide of SDK, troubleshooting tips and development environment setup. Reading the Kinect device information, monitoring and notifying the sensor status change. Automatically ...

Global site tag (gtag.js) - Google Analytics