Jump to content

ROSE Compiler Framework/Good API Design

From Wikibooks, open books for an open world

Google: "How to Design a Good API and Why it Matters" by Joshua Bloch

TODO: convert from Markdown

Characteristics of a Good API

[edit | edit source]
  • Easy to learn
  • Easy to use, even without documentation
  • Hard to misuse
  • Easy to read and maintain code that uses it
  • Sufficiently powerful to satisfy requirements
  • Easy to extend
  • Appropriate to audience

The Process of API Design

[edit | edit source]

[T]he repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards.

    • Doubles as examples/tutorials and unit tests
  • Maintain realistic expectations
    • You won't be able to please everyone... aim to displease everyone equally
    • Expect to evolve API; mistakes happen; real-world usage is necessary

General Principles

[edit | edit source]
  > [A] measurement of actual performance [power / weight]
  • Don't give users a gun to shoot themselves with
    • Information hiding: minimize the accessibility of everything

Documentation Matters

[edit | edit source]
  • Class: what an instance represents
* Method: contract between method and calling client (preconditions, postconditions, and side-effects)
* Parameter: indicate units, form, ownership

Pre- and Post- Conditions

  • The precondition statement indicates what must be true before the function is called.
  • The postcondition statement indicates what will be true when the function finishes its work.
  /// \post <return_value>.empty() == false
  

API vs. Implementation

[edit | edit source]

Implementation details should not impact the API. Don't let implementation details "leak" into the API.

Performance

  • Design for usability, refactor for performance
  • Do not warp the API to gain performance
  • Effects of API design decisions on performance are real and permanent:
    • Component.getSize() returns Dimension
    • Dimension is mutable
    • Each getSize call must allocate Dimension
    • Causes millions of needless object allocations

"Harmonize"

[edit | edit source]
  • API must coexist peacefully with platform
    • Do what is customary (standard)
    • Avoid obsolete parameter and return types
    • Mimic patterns in core APIs and language
    • Take advantage of API-friendly features: generics, varargs, enums, default arguments
  • Don't make the client do anything the module could do
    • Reduce need for boilerplate code
  • Don't violate the [Principle of Least Astonishment](http://en.wikipedia.org/wiki/Principle_of_least_astonishment)
  > The design should match the user's experience, expectations, and mental models...aims to exploit users' pre-existing knowledge as a way to minimize the learning curve
  • Provide programmatic access to all data available in string form => no client string parsing necessary
  • Overload with care: ambiguous overloadings

Names Matter

[edit | edit source]
  • Largely self-explanatory (avoid cryptic abbreviations)
  • Be consistent (e.g. same word means same thing)
  • Strive for symmetry
  • Should read like [prose](http://en.wikipedia.org/wiki/Prose)
 > [T]he most typical form of language, applying ordinary grammatical structure and natural flow of speech rather than rhythmic structure (as in traditional poetry).
      if (car.speed() > 2 * SPEED_LIMIT)
        generateAlert("Watch out for cops!");
    

Input Parameters

[edit | edit source]
  • interface types over classes: flexibility, performance
  • most specific possible type: moves error from runtime to compile time
  • use double (64 bits) rather than float (32 bits): precision loss is real, performance loss negligible
  • consistent ordering:
      #include <string.h>
      char *strcpy (char *dest, char *src);
      void bcopy   (void *src,  void *dst, int n); // bad!
    
  • short parameter lists: 3 or fewer; more and users will have to refer to docs; identically typed params harmful
    • Two techniques for shortening: 1) break up method, 2) create helper class to hold parameters

Return Values

[edit | edit source]
  • Avoid values that demand exceptional processing
 > For example, return a `zero-length array` or `empty collection`, not `null`

Exceptions

[edit | edit source]
  • don't force client to use exceptions for control flow
  • don't fail silently
  • favor unchecked exceptions
  • include failure-capture diagnostic information
  • Fail fast: report errors ASAP
    • Compile time: static typing, generics
    • Run time: error on first bad method invocation (should be failure-atomic)