How to Design a Good API and Why it Matters

为什么API设计很重要?API可能是一个公司/一个产品最重要的资产,你的客户可能会因为它好用,开始学习,付费购买并使用它们,并让客户们觉得离开你的API是需要付出巨大代价的,好的API设计会轻易俘获客户的心。当然API也可能会成为一个公司最大的债务,拙略的API设计可能会让你疲于应付你的用户支持。请记住,公共API一旦定义,就会一直被使用,你只有一次机会,那就是在最开始的时候把它定义设计好。如何设计好的API,良好的API设计应当具有哪些特征?通常的设计原则是什么?来自Google的首席软件工程师的经验之谈,献给从事软件开发的你。
展开查看详情

1. How to Design a Good API and Why it Matters Joshua Bloch Principal Software Engineer 1 _How to Design a Good API and Why it Matters

2. Why is API Design Important? • APIs can be among a company's greatest assets _ Customers invest heavily: buying, writing, learning _ Cost to stop using an API can be prohibitive _ Successful public APIs capture customers • Can also be among company's greatest liabilities _ Bad APIs result in unending stream of support calls • Public APIs are forever - one chance to get it right 2 _ How to Design a Good API and Why it Matters

3. Why is API Design Important to You? • If you program, you are an API designer _ Good code is modular–each module has an API • Useful modules tend to get reused _ Once module has users, can’t change API at will _ Good reusable modules are corporate assets • Thinking in terms of APIs improves code quality 3 _ How to Design a Good API and Why it Matters

4. Characteristics of a Good API • 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 4 _ How to Design a Good API and Why it Matters

5. Outline I. The Process of API Design II. General Principles III. Class Design IV. Method Design V. Exception Design VI. Refactoring API Designs 5 _ How to Design a Good API and Why it Matters

6. I. The Process of API Design 6 _ How to Design a Good API and Why it Matters

7. Gather Requirements–with a Healthy Degree of Skepticism • Often you'll get proposed solutions instead _ Better solutions may exist • Your job is to extract true requirements _ Should take the form of use-cases • Can be easier and more rewarding to build something more general Good 7 _ How to Design a Good API and Why it Matters

8. Start with Short Spec–1 Page is Ideal • At this stage, agility trumps completeness • Bounce spec off as many people as possible _ Listen to their input and take it seriously • If you keep the spec short, it’s easy to modify • Flesh it out as you gain confidence _ This necessarily involves coding 8 _How to Design a Good API and Why it Matters

9. Write to Your API Early and Often • Start before you've implemented the API _ Saves you doing implementation you'll throw away • Start before you've even specified it properly _ Saves you from writing specs you'll throw away • Continue writing to API as you flesh it out _ Prevents nasty surprises _ Code lives on as examples, unit tests 9 _How to Design a Good API and Why it Matters

10. Writing to SPI is Even More Important • Service Provider Interface (SPI) _ Plug-in interface enabling multiple implementations _ Example: Java Cryptography Extension (JCE) • Write multiple plug-ins before release _ If you write one, it probably won't support another _ If you write two, it will support more with difficulty _ If you write three, it will work fine • Will Tracz calls this “The Rule of Threes” (Confessions of a Used Program Salesman, Addison-Wesley, 1995) Bad 10 _How to Design a Good API and Why it Matters

11. Maintain Realistic Expectations • Most API designs are over-constrained _ You won't be able to please everyone _ Aim to displease everyone equally • Expect to make mistakes _ A few years of real-world use will flush them out _ Expect to evolve API 11 _How to Design a Good API and Why it Matters

12. II. General Principles 12 _ How to Design a Good API and Why it Matters

13. API Should Do One Thing and Do it Well • Functionality should be easy to explain _ If it's hard to name, that's generally a bad sign _ Good names drive development _ Be amenable to splitting and merging modules 13 _How to Design a Good API and Why it Matters

14. API Should Be As Small As Possible But No Smaller • API should satisfy its requirements • When in doubt leave it out _ Functionality, classes, methods, parameters, etc. _ You can always add, but you can never remove • Conceptual weight more important than bulk • Look for a good power-to-weight ratio 14 _ How to Design a Good API and Why it Matters

15. Implementation Should Not Impact API • Implementation details _ Confuse users _ Inhibit freedom to change implementation • Be aware of what is an implementation detail _ Do not overspecify the behavior of methods _ For example: do not specify hash functions _ All tuning parameters are suspect • Don't let implementation details “leak” into API _ On-disk and on-the-wire formats, exceptions 15 _ How to Design a Good API and Why it Matters

16. Minimize Accessibility of Everything • Make classes and members as private as possible • Public classes should have no public fields (with the exception of constants) • This maximizes information hiding • Allows modules to be used, understood, built, tested, and debugged independently 16 _How to Design a Good API and Why it Matters

17. Names Matter–API is a Little Language • Names Should Be Largely Self-Explanatory _ Avoid cryptic abbreviations • Be consistent–same word means same thing _ Throughout API, (Across APIs on the platform) • Be regular–strive for symmetry • Code should read like prose if (car.speed() > 2 * SPEED_LIMIT) generateAlert("Watch out for cops!"); 17 _How to Design a Good API and Why it Matters

18. Documentation Matters Reuse is something that is far easier to say than to do. Doing it requires both good design and very good documentation. Even when we see good design, which is still infrequently, we won't see the components reused without good documentation. - D. L. Parnas, _Software Aging. Proceedings of 16th International Conference Software Engineering, 1994 18 _ How to Design a Good API and Why it Matters

19. Document Religiously • Document every class, interface, method, constructor, parameter, and exception _ Class: what an instance represents _ Method: contract between method and its client _ Preconditions, postconditions, side-effects _ Parameter: indicate units, form, ownership • Document state space very carefully 19 _ How to Design a Good API and Why it Matters

20. Consider Performance Consequences of API Design Decisions • Bad decisions can limit performance _ Making type mutable _ Providing constructor instead of static factory _ Using implementation type instead of interface • Do not warp API to gain performance _ Underlying performance issue will get fixed, but headaches will be with you forever _ Good design usually coincides with good performance 20 _ How to Design a Good API and Why it Matters

21. 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 • Alternative added in 1.2; old client code still slow 21 _ How to Design a Good API and Why it Matters

22. API Must Coexist Peacefully with Platform • Do what is customary _ Obey standard naming conventions _ Avoid obsolete parameter and return types _ Mimic patterns in core APIs and language • Take advantage of API-friendly features _ Generics, varargs, enums, default arguments • Know and avoid API traps and pitfalls _ Finalizers, public static final arrays 22 _How to Design a Good API and Why it Matters

23. III. Class Design 23 _ How to Design a Good API and Why it Matters

24. Minimize Mutability • Classes should be immutable unless there’s a good reason to do otherwise _ Advantages: simple, thread-safe, reusable _ Disadvantage: separate object for each value • If mutable, keep state-space small, well-defined _ Make clear when it's legal to call which method Bad: Date, Calendar Good: TimerTask 24 _How to Design a Good API and Why it Matters

25. Subclass Only Where It Makes Sense • Subclassing implies substitutability (Liskov) _ Subclass only when is-a relationship exists _ Otherwise, use composition • Public classes should not subclass other public classes for ease of implementation Bad: Properties extends Hashtable Stack extends Vector Good: Set extends Collection 25 _ How to Design a Good API and Why it Matters

26. Design and Document for Inheritance or Else Prohibit it • Inheritance violates encapsulation (Snyder, ‘86) _ Subclass sensitive to implementation details of superclass • If you allow subclassing, document self-use _ How do methods use one another? • Conservative policy: all concrete classes final Bad: Many concrete classes in J2SE libraries Good: AbstractSet, AbstractMap 26 _ How to Design a Good API and Why it Matters

27. IV. Method Design 27 _ How to Design a Good API and Why it Matters

28. Don't Make the Client Do Anything the Module Could Do • Reduce need for boilerplate code _ Generally done via cut-and-paste _ Ugly, annoying, and error-prone import org.w3c.dom.*; import java.io.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; // DOM code to write an XML document to a specified output stream. private static final void writeDoc(Document doc, OutputStream out)throws IOException{ try { Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); t.transform(new DOMSource(doc), new StreamResult(out)); } catch(TransformerException e) { throw new AssertionError(e); // Can’t happen! } } 28 _ How to Design a Good API and Why it Matters

29. Don't Violate the Principle of Least Astonishment • User of API should not be surprised by behavior _ It's worth extra implementation effort _ It's even worth reduced performance public class Thread implements Runnable { // Tests whether current thread has been interrupted. // Clears the interrupted status of current thread. public static boolean interrupted(); } 29 _How to Design a Good API and Why it Matters