关闭

Google首席工程师Joshua Bloch谈如何设计优秀的API

标签: 如何设计优秀的APIGoogle Joshua Bloch首席工程师Joshua Bloch
1180人阅读 评论(1) 收藏 举报
分类:

Google首席工程师Joshua Bloch谈如何设计优秀的API


How to Design a Good API and Why it Matters

中文版:http://www.codeceo.com/article/google-java-good-api.html


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


Why is API Design Importantto 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


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

Outline

I. The Process of API Design
II. General Principles
III. Class Design
IV. Method Design
V. Exception Design
VI. Refactoring API Designs


The Process of API Design


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 ofuse-cases
  • Can be easier and more rewarding to build something more general


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


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




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)


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



General Principles

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


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



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



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



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!");



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



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



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




Effects of API Design Decisions on Performance are Real and Permanent

Component.getSize()returns Dimension
Dimension is mutable
EachgetSize call must allocateDimension
Causes millions of needless object allocations
Alternative added in 1.2; old client code still slow




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




Class Design

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




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



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, documentself-use
_How do methods use one another?
Conservative policy: all concrete classes final

Bad: Many concrete classes in J2SE libraries
Good: AbstractSet, AbstractMap


Method Design

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!
}
}





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();
}




Fail Fast–Report Errors as Soon as Possible After They Occur

Compile time is best - static typing, generics
At runtime, first bad method invocation is best
_ Method should be failure-atomic
// A Properties instance maps strings to strings
public class Properties extends Hashtable {
public Object put(Object key, Object value);
// Throws ClassCastException if this properties
// contains any keys or values that are not strings
public void save(OutputStream out, String comments);
}


Provide Programmatic Access to All Data Available in String Form

Otherwise, clients will parse strings
_ Painful for clients
_ Worse, turns string format into de facto API
public class Throwable {
public void printStackTrace(PrintStream s);
public StackTraceElement[] getStackTrace(); // Since 1.4
}
public final class StackTraceElement {
public String getFileName();
public int getLineNumber();
public String getClassName();
public String getMethodName();
public boolean isNativeMethod();
}



Overload With Care

Avoid ambiguous overloadings
_ Multiple overloadings applicable to same actuals
Conservative: no two with same number of args
Just because you can doesn't mean you should
_Often better to use a different name
If you must provide ambiguous overloadings, ensure same behavior for same arguments
public TreeSet(Collection c); // Ignores order
public TreeSet(SortedSet s); // Respects order



Use Appropriate Parameter and Return Types

Favor interface types over classes for input
_Provides flexibility, performance
Use most specific possible input parameter type
_ Moves error from runtime to compile time
Don't use string if a better type exists
_ Strings are cumbersome, error-prone, and slow
Don't use floating point for monetary values
_ Binary floating point causes inexact results!
Use double (64 bits) rather than float (32 bits)
_ Precision loss is real, performance loss negligible




Use Consistent Parameter Ordering Across Methods

Especially important if parameter types identical
#include <string.h>
char *strcpy (char *dest, char *src);
void bcopy (void *src, void *dst, int n);
java.util.Collections – first parameter always
collection to be modified or queried
java.util.concurrent – time always specified as
long delay, TimeUnit unit



Avoid Long Parameter Lists

Three or fewer parameters is ideal
_ More and users will have to refer to docs
• Long lists of identically typed params harmful
_ Programmers transpose parameters by mistake
_ Programs still compile, run, but misbehave!
Two techniques for shortening parameter lists
_ Break up method
_ Create helper class to hold parameters
// Eleven parameters including four consecutive ints
HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName,
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU hMenu, HINSTANCE hInstance,
LPVOID lpParam);




Avoid Return Values that Demand Exceptional Processing

return zero-length array or empty collection, not null

package java.awt.image;
public interface BufferedImageOp {
// Returns the rendering hints for this operation,
// or null if no hints have been set.
public RenderingHints getRenderingHints();
}



Exception Design

Throw Exceptions to Indicate Exceptional Conditions

Don’t force client to use exceptions for control flow
private byte[] a = new byte[BUF_SIZE];
void processBuffer (ByteBuffer buf) {
try {
while (true) {
buf.get(a);
processBytes(tmp, BUF_SIZE);
}
} catch (BufferUnderflowException e) {
int remaining = buf.remaining();
buf.get(a, 0, remaining);
processBytes(bufArray, remaining);
}
}
Conversely, don’t fail silently
ThreadGroup.enumerate(Thread[] list)



Favor Unchecked Exceptions

Checked – client must take recovery action
Unchecked – programming error
Overuse of checked exceptions causes boilerplate

try {
Foo f = (Foo) super.clone();
....
} catch (CloneNotSupportedException e) {
// This can't happen, since we’re Cloneable
throw new AssertionError();
}




Include Failure-Capture Information in Exceptions

Allows diagnosis and repair or recovery
For unchecked exceptions, message suffices
For checked exceptions, provide accessors


Refactoring API Designs


Sublist Operations in Vector

public class Vector {
public int indexOf(Object elem, int index);
public int lastIndexOf(Object elem, int index);
...
}
Not very powerful - supports only search
Hard too use without documentation



Sublist Operations Refactored

public interface List {
List subList(int fromIndex, int toIndex);
...
}
Extremely powerful - supports all operations
Use of interface reduces conceptual weight
_ High power-to-weight ratio
Easy to use without documentation



Thread-Local Variables

// Broken - inappropriate use of String as capability.
// Keys constitute a shared global namespace.
public class ThreadLocal {
private ThreadLocal() { } // Non-instantiable
// Sets current thread’s value for named variable.
public static void set(String key, Object value);
// Returns current thread’s value for named variable.
public static Object get(String key);
}




Thread-Local Variables Refactored (1)

public class ThreadLocal {
private ThreadLocal() { } // Noninstantiable
public static class Key { Key() { } }
// Generates a unique, unforgeable key
public static Key getKey() { return new Key(); }
public static void set(Key key, Object value);
public static Object get(Key key);
}
Works, but requires boilerplate code to use
static ThreadLocal.Key serialNumberKey = ThreadLocal.getKey();
ThreadLocal.set(serialNumberKey, nextSerialNumber());
System.out.println(ThreadLocal.get(serialNumberKey));



Thread-Local Variables Refactored (2)

public class ThreadLocal {
public ThreadLocal() { }
public void set(Object value);
public Object get();
}
Removes clutter from API and client code
static ThreadLocal serialNumber = new ThreadLocal();
serialNumber.set(nextSerialNumber());
System.out.println(serialNumber.get());



Conclusion

API design is a noble and rewarding craft
_ Improves the lot of programmers, end-users, companies
This talk covered some heuristics of the craft
_ Don't adhere to them slavishly, but...
_Don't violate them without good reason
API design is tough
_ Not a solitary activity
Perfection is unachievable, but try anyway





1
0
查看评论

Java教父Joshua Bloch访谈

Sun公司网站登出了2007 JavaOne大会期间对Joshua Bloch的访谈——摇滚巨星Josh Bloch。 Bloch现任Google 首席Java架构师,而在此之前,他是Sun公司的杰出工程师,领导了大量Java平台特性的设计和实现,包括Java SE 5.0版本和广受赞誉的Java ...
  • turingbook
  • turingbook
  • 2007-06-10 22:38
  • 3937

Joshua Bloch离开Google了,Dart前景堪忧

Joshua Bloch,Java界的传奇人物,如果你没有听过他的名字,也肯定看过他的书(Effective Java,Java Concurrency in Practice, Java Puzzlers)。他在哥伦比亚大学和卡内基梅隆大学分别获得计算机本科和博士学位。先是在Sun公司工作,200...
  • qinjienj
  • qinjienj
  • 2012-09-21 22:42
  • 4421

采访Java Collection的作者Joshua Bloch

想必用Java的人都用过JDK的容器类吧,什么List, Set, Map啦。每天这些代码在全世界成千上万的JVM里面运行,每天数以万记的程序员在使用这些类。你知道这些这么cool的代码是谁写的吗?是Joshua Bloch。他以前是在Sun工作,现在跳到Google了,Google是无敌了。他可是...
  • michaellufhl
  • michaellufhl
  • 2010-09-16 23:25
  • 2770

elasticsearch-query-builder, 一款可以基于配置化以及参数绑定的ES语句构造神器

前言 在这里,我想向大家推荐一个我自己开发的项目,也就是elasticsearch-query-builder,这个项目目前在github上已经开源,有兴趣的朋友可以去fork或者star,你的star就是对我最大的鼓励。同时,本项目长期维护和更新,我也接受并且很高兴有小伙伴向本项目pull ...
  • xiaowei1118
  • xiaowei1118
  • 2017-12-12 17:56
  • 68

Java解惑(Joshua Bloch).pdf

  • 2013-04-04 21:05
  • 876KB
  • 下载

Google首席工程师Joshua Bloch谈如何设计优秀的API

Google首席工程师Joshua Bloch谈如何设计优秀的API How to Design a Good API and Why it Matters Why is API Design Important? APIs can be among a company...
  • doctor_who2004
  • doctor_who2004
  • 2016-07-24 19:45
  • 1180

Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API【附PPT】

http://www.csdn.net/article/2014-02-18/2818441-How-to-design-a-good-API   Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API【附PPT】 作者Joshua Bloch API...
  • sunjianjun1981
  • sunjianjun1981
  • 2014-02-18 09:34
  • 728

Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API

【编者按】随着近来软件规模的日益庞大,API编程接口的设计变的越来越重要。良好的接口设计可以降低系统各部分之间的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合度,从而提高系统的维护性和稳定性。 Joshua Bloch是美国著名程序式设计师。他为Java平台设计并实现了许多的功能,是Go...
  • whgggg
  • whgggg
  • 2014-02-18 09:17
  • 411

Joshua Bloch新作《Java解惑》即将上市——大家一起来做运动,Java思维体操!

Java脑力体操,Java解惑汇评 英雄所见略同。Joshua Bloch的《Java解惑》(Java Puzzlers)出版后,好评不断。这里是一个汇集: ...
  • turingbook
  • turingbook
  • 2006-01-11 23:55
  • 2327

Joshua Bloch访谈:API对设计流程的影响

本文是Common Lisp专家Peter Seibel对Google公司首席Java架构师Joshua Bloch的访谈,谈到API对设计流程的影响和Google的Java观,以及数学、散文与程序员的关系。 数学与程序员的关系 Seibel:你认识有什么伟...
  • jiang_bing
  • jiang_bing
  • 2012-05-28 16:44
  • 388
    个人资料
    • 访问:715644次
    • 积分:9620
    • 等级:
    • 排名:第2202名
    • 原创:256篇
    • 转载:184篇
    • 译文:9篇
    • 评论:71条
    博客专栏
    博客链接