1 尽量少发布API,把不必要接口移走,尤其是外部不该用的内部处理方法,一旦发布你就必须提供后续兼容服务。
2 方法优于字段,发布API一定不能直接发布字段,发布方法可以隐藏处理字段信息。
3 工厂方法优于构造函数,如果发布一个构造函数,返回的一定是固定的实例,而不会是子类,无法扩展,还能进行实例缓存,做统一处理。相反,构造方法不能动态扩展。
4 如果对外发布类,并且没有设计子类需求,那就让这个类不可继承。一旦发布出去,用户可能会继承,重写,调用处理等方式完全改变原有的行为和设计意图,一旦原有的类方法内容发生改变,可能会导致用户子类错误,影响很大。还可能引出更多的麻烦(尤其用户把继承后的类传进来),破坏系统设定。还可以不公开构造方法使用工厂模式发布类。
针对API使用者,最好使用组合代理等方式扩展,谨慎使用继承。
5 谨慎发布setter方法,考虑是否setter方法值得被使用。
6 富裕对象更多权力
a JavaBean简单控制权限,添加一个 public void setReadOnly()方法,同时 修改setter方法,使它调用后抛异常.
b 如果工厂方法参数过长,可以封装参数类,参数类里只有setter方法
public static Executor create(Configration config){
return new Fair(config);
}
public static final class Configration{
publi void setXX(boolean){
}
}
封装参数类还能根据不同的持有者,传递到相同的控制类进而做出不同的反应。如权限。
7 避免暴露深层次继承。继承不是改变具体的行为还是增加额外的行为。可以通过实现接口来避免不必要继承。
8 用户不要依赖非正式途径方式访问资源(滥用反射)
9 添加方法另一种方案。接口一旦定下来就很难改变。如果接口更改,客户端要改很多东西,对于接口增加的新方法,客户端可能要针对版本进行判断。为了避免if else ,可以在客户端增加一个final 类,把接口注入这个类里,类里做了一层代理,把接口改变的影响全部封装在这个类里。
10 要为增加参数做准备(request/reponse模式)
11 模块化架构(ServiceLoader简单实现依赖注入)
12 设计API时要区分目标用户(API/SPI)
SPI类似与serviceload,提供给用户/第三方实现,用户希望通过实现这些接口来扩展功能,原则上API发布后不允许删除方法,SPI发布后不允许增加方法。
13 API要有可测试性。模块级API实现要考虑提供TCK(兼容性测试套件),它是多个测试项的集合,API实现人员可以用它验证实现是否正确,特别是对各种期望方法是否有一致性。
14 不要暴露第三方API,第三方不确定性很大。
15 不要暴露太多,以免用户拿到更多内容放大效果。如暴露文件本身不如暴露文件信息安全(可以提供一个流/FileObject,允许API用户获得信息)。
16 如果约束一个方法的行为,最好的方式就是避免这个方法被覆盖,如果要约束一大堆方法的一致性,最好的方式就是让相应的类变成final类,然后在自己代码内部检查一致性。扩展性,其实就是通过实现额外的接口,并结合工厂方法拿到接口具体实现。
public abstract class Lookup{
Lookup(){
}
public <T> T lookup(Class<T> clazz){
Itearator<T> it = doLookup(clazz);
return it.hasNet() ? it.next() : null;
}
public <T> Collection<? extends T> lookupAll(Class<T> clazz){
Iterator<T> it = doLookup(clazz);
if(!it.hasNext()){
return Collections.emptyList();
}
else{
List<T> result = new ArrayList<T>();
while(it.hasNext()){
result.add(it.next());
}
return result;
}
}
}
17 设计API时,方法访问级别的语义
public 允许外部代码调用 子类可以覆盖或者调用
public abstract 子类必须实现该方法 允许外部调用
public final 允许外部调用
protected 允许子类调用 子类可以覆盖
protected abstract 子类必须实现
protected final 允许子类调用