随着大数据、公共平台等互联网技术的日益成熟,API接口的重要性日益凸显,从公司的角度来看,API能够算做是公司一笔巨大的资产,公共API能够捕获用户、为公司作出许多贡献。对于我的来讲,只要你编程,你就是一个API设计者,由于好的代码便是模块——每一个模块即是一个API,而好的模块会被屡次使用。此外,编写API还有利于开发者提升代码质量,提升自身的编码水平。java
优秀API所具有的特征:程序员
了解了一款优秀API所具有的特征后,一块儿再来看看如何设计优秀的API,有哪些流程和规则可循,开发者在设计时须要注意哪些事项。编程
征集需求数组
在开始以前,你可能会收到一些解决方案,它们不必定会比现有的方案好,而你的任务是以用例的形式提取真实需求,并制定真正合适的解决方案,这样构建出来的东西就会更加有价值。app
从简短的说明开始dom
这时,编写简短的说明最为合适,编写时须要考虑的因素有:ide
尽早编写API函数
编写SPI尤其重要性能
维护切实可行的指望测试
每一个API接口应该只专一一件事,并作好:若是它很难命名,那么这或许是个很差的征兆,好的名称能够驱动开发、而且只需拆分与合并模块便可
if (car.speed() > 2 * SPEED_LIMIT) generateAlert("Watch out for cops!");
重视文档
开发API时要意识到文档的重要性。组件重用不是纸上谈兵的东西,既须要好的设计,也须要优秀的文档,这两者缺一不可,即便咱们看到了良好的设计而未见文档,那么组件重用也是不妥的。
文档应包含每一个类、接口、方法、构造函数、参数和异常,此外,还要当心对待文档的状态空间。
API设计决策对性能的影响
API与平台和平共处
最小化可变性
子类只存在有意义的地方
用于继承的设计和文档或者直接禁止继承(Design and Document for Inheritance or Else Prohibit it)
模块能作到的,客户端就不要作减小模板代码的使用:
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! } }
遵照最小惊讶原则
用户API只需根据需求来设计便可,没必要让客户感到惊讶,当心弄巧成拙:
public class Thread implements Runnable { // Tests whether current thread has been interrupted. // Clears the interrupted status of current thread. public static boolean interrupted(); }
故障快速报告应尽快生成
// 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); }
以String形式对全部可用数据提供编程式访问
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();}
方法重载要细心
public TreeSet(Collection c); // Ignores orderpublic TreeSet(SortedSet s); // Respects order
使用合适的参数和返回类型
#include char *strcpy (char *dest, char *src); void bcopy (void *src, void *dst, int n);
ava.util.Collections – first parameter always collection to be modifiedor queriedjava.util.concurrent – time always specified as long delay, TimeUnitunit
避免使用长参数列表
最好避免这种状况出现:
// Eleven parameters including four consecutive intsHWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
返回值勿需进行异常处理
好比,返回零长度字符串或者空集合
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(); }
抛出异常来讲明异常情况;不要强迫客户端使用异常来控制流。
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)
支持Unchecked Exceptions
try { Foo f = (Foo) super.clone(); ....} catch (CloneNotSupportedException e) { // This can't happen, since we’re Cloneable throw new AssertionError();}
异常中应该包含捕获错误的(Failure-Capture)信息
在Vector中进行Sublist操做
public class Vector { public int indexOf(Object elem, int index); public int lastIndexOf(Object elem, int index); ...}
分析:
重构Sublist操做
public interface List { List subList(int fromIndex, int toIndex); ...}
分析:
线程局部变量
// 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); }
线程局部变量重构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); }
能够运行,可是须要使用模板代码。
static ThreadLocal.Key serialNumberKey = ThreadLocal.getKey(); ThreadLocal.set(serialNumberKey, nextSerialNumber()); System.out.println(ThreadLocal.get(serialNumberKey));
线程局部变量重构2
public class ThreadLocal { public ThreadLocal() { } public void set(Object value); public Object get(); }
从API和客户端代码中删除了无用代码。
static ThreadLocal serialNumber = new ThreadLocal(); serialNumber.set(nextSerialNumber()); System.out.println(serialNumber.get());
总结
API设计是一件很是高端大气上档次的工艺,对程序员、终端用户和公司都会有所提高。不要盲目地去遵照文中所说起的规则、说明等,但也不要去侵犯他们,API设计不是件简单的工艺,也不是一种能够孤立行动的活。固然完美永远没法实现,但咱们要努力去追求完美。
写在最后:欢迎留言讨论,加关注,持续更新!!!