• How to Design a Good API and Why it Matters


    前谷歌首席 Java 架构师谈如何设优秀的 API – 码农网 http://www.codeceo.com/article/google-java-good-api.html

    随着近来软件规模的日益庞大,API编程接口的设计变的越来越重要。良好的接口设计可以降低系统各部分之间的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合度,从而提高系统的维护性和稳定性。

    Joshua Bloch是美国著名程序式设计师。他为Java平台设计并实现了许多的功能,是Google的首席Java架构师(Chief Java Architect)。他也是《Effective Java Programming Language Guide》一书的作者,就是人们常说的 Effective Java。本文翻译自Joshua Bloch所发表的一个PPT:How to Design a Good API and Why it Matters

    随着大数据、公共平台等互联网技术的日益成熟,API接口的重要性日益凸显,从公司的角度来看,API可以算作是公司一笔巨大的资产,公共API可以捕获用户、为公司做出许多贡献。对于个人来说,只要你编程,你就是一个API设计者,因为好的代码即是模块——每个模块便是一个API,而好的模块会被多次使用。此外,编写API还有利于开发者提高代码质量,提高自身的编码水平。

    优秀API所具备的特征:

    • 简单易学;
    • 易于使用,即使没有文档;
    • 很难误用;
    • 易于阅读,代码易于维护;
    • 足够强大,可以满足需求;
    • 易于扩展;
    • 适合用户。

    了解了一款优秀API所具备的特征后,一起再来看看如何设计优秀的API,有哪些流程和规则可循,开发者在设计时需要注意哪些事项。

    API设计流程中的注意事项

    征集需求

    在开始之前,你可能会收到一些解决方案,它们不一定会比现有的方案好,而你的任务是以用例的形式提取真实需求,并制定真正合适的解决方案,这样构建出来的东西就会更加有价值。

    从简短的说明开始

    这时,编写简短的说明最为合适,编写时需要考虑的因素有:

    • 灵活性要远胜于完整性;
    • 跳出规则:听取意见并严阵以待;
    • 精炼短小才易修改;
    • 获得信任之后将其具体化,在此之中,编程很重要。

    尽早编写API

    • 对每一个实现进行保存,以防丢失;
    • 在开始之前,列出一些合理的规定,保存所写说明,以防丢失;
    • 继续编写和充实API。

    编写SPI尤为重要

    • Service Provider Interface即服务提供商接口,插件服务支持多种实现,例如Java Cryptography Extension
      (JCE);
    • 发布之前编写多个插件;
    • “三次原则”(“The Rule of Threes”):指的是当某个功能第三次出现时,才进行”抽象化”。

    维护切实可行的期望

    • 大多数API设计都过于约束;
    • 对可能会犯的错误进行预计,要用发展的思维来编写API。

    API设计原则

    每个API接口应该只专注一件事,并做好:如果它很难命名,那么这或许是个不好的征兆,好的名称可以驱动开发、并且只需拆分与合并模块即可

    • API应尽可能地轻小:满足需求、对有疑问的地方可以暂时不使用(函数、类、方法、参数等,你可以不添加,但千万不要删除)、概念性的东西比体积重要、寻找一个良好的动力体积比;
    • 实现不要影响API:关注实现细节(不要迷惑用户、不要随便改变实现方式)、意识到具体的实现细节(不要有越权的方法行为,例如不要制订哈希函数、所有的调优参数都是可疑的);
    • 不要让实现细节“泄露”到API(例如on-disk和on-the-wire格式等异常情况);
    • 最小化可访问:设计人员应尽量把类及成员设为私有,公共类不应该有公共字段(包括异常实例),最大限度地提高信息隐藏,允许模块可以被使用、理解、构建、测试和独立调试;
    • 命名问题:应该见名知意,避免含糊的缩写、对同一样东西的命名应该有个一致性的前缀(遍及整个平台API)、讲究对称、代码应该易读。如下所示:
    if (car.speed() > 2 * SPEED_LIMIT)
     generateAlert("Watch out for cops!");

    重视文档

    开发API时要意识到文档的重要性。组件重用不是纸上谈兵的东西,既需要好的设计,也需要优秀的文档,这二者缺一不可,即使我们看到了良好的设计而未见文档,那么组件重用也是不妥的。

    ——摘自 D. L. Parnas 在1994年第16届国际软件开发大会上的演讲内容

    文档应包含每个类、接口、方法、构造函数、参数和异常,此外,还要小心对待文档的状态空间。

    API设计决策对性能的影响

    • API设计决策对性能的影响是真实永久的;
    • 不好的决策会限制性能(类型易变、构造函数替代静态工厂、实现类型替代接口);
    • 不得打包API来提升性能(潜在的性能问题可能会得到修复,但救的了一时,救不了一世);
    • 良好的设计通常与好的性能是一致的。

    API与平台和平共处

    • 养成良好的习惯:遵守标准的命名约定、避免陈旧的参数和返回类型、核心API和语言的模仿模式;
    • 利用API的友好功能:泛型、可变参数、枚举、默认参数;
    • 了解和避免API陷阱和缺陷:Finalizers、公共静态Final数组。

    API中类的设计

    最小化可变性

    • 最好不要随便改变类,除非有一个非常合理的理由;
    • 如果是可变类,最好保持很小的状态空间、定义良好的结构,因时制宜地去调用方法。

    子类只存在有意义的地方

    • 子类具备可替代性(Liskov);
    • 公共类不应该继承其它公共类。

    用于继承的设计和文档或者直接禁止继承(Design and Document for Inheritance or Else Prohibit it

    • 继承破坏封装
    • 如果你允许子类和文档自用,那么要考虑彼此该如何互相调用方法
    • 保守策略:把所有类都设置成Final

    API中的方法设计

    模块能做到的,客户端就不要做减少模板代码的使用:

    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 order
    public TreeSet(SortedSet s); // Respects order

    使用合适的参数和返回类型

    • 通过类来支持接口类型输入
    • 尽可能地使用最特定的输入参数类型
    • 如果已经有一个更好的类型存在,就不要使用string类型
    • 不要用浮点型来修饰货币值
    • 使用Double(64位)而不要使用Float(32位)
    • 在方法上参数顺序要一致,尤其是参数类型相同时,则尤为重要
    #include 
     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

    避免使用长参数列表

    • 三个或三个以内的参数是最完美的
    • 长参数列表是有害的,程序员容易出错,并且程序在编译、运行时会表现不好
    • 缩短参数的两种方法:Break up method、创建参数助手类

    最好避免这种情况出现:

    // 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);

    返回值勿需进行异常处理

    比如,返回零长度字符串或者空集合

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

    API中的异常设计

    抛出异常来说明异常状况;不要强迫客户端使用异常来控制流。

    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

    • Checked——客户端肯定会做一些恢复措施
    • Unchecked——编程错误
    • 过度使用Checked异常会产生一些模板代码
    try {
     Foo f = (Foo) super.clone();
     ....
    } catch (CloneNotSupportedException e) {
     // This can't happen, since we’re Cloneable
     throw new AssertionError();
    }

    异常中应该包含捕获错误的(Failure-Capture)信息

    • 允许诊断和修复或恢复
    • 对于Unchecked异常,有异常消息就行了
    • 对于Checked异常,提供访问器

    重构API设计

    在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);
     ...
    }

    分析:

    • 非常强大——支持所有操作
    • 使用接口来减少概念权重:较高的动力重量(power-to-weight)比
    • 没有文档也易于使用

    线程局部变量

     // 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设计不是件简单的工艺,也不是一种可以孤立行动的活。当然完美永远无法实现,但我们要努力去追求完美。

    Google首席工程师Joshua Bloch谈如何设计优秀的API - Beaver - CSDN博客 https://blog.csdn.net/doctor_who2004/article/details/52014667

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

  • 相关阅读:
    分布式id生成
    DB主从一致性架构优化4种方法
    Mysql在大型网站的应用架构演变
    win10下iis绑定局域网ip无效的解决方案
    css随笔
    html标签说明
    C# JSON序列化日期格式问题
    使用事件机制相比直接调用函数的优势
    C#绑定事件时使用匿名函数
    C# t4模版引擎笔记 引入外部dll
  • 原文地址:https://www.cnblogs.com/rsapaper/p/9930793.html
Copyright © 2020-2023  润新知