[憨读记 之 Effective Java] 01-用静态工厂方法代替构造器

书的第一章是建立和销毁对象,接下来的几篇也都是围绕这个展开。java

本篇对应书中的第一条:用静态工厂方法代替构造器。数据库

什么是静态工厂方法

先看一个例子,Boolean类中有以下构造器编程

public Boolean(boolean value) {
    this.value = value;
}

同时,还提供了以下的静态方法,也能够返回Boolean类实例设计模式

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

这个就是静态工厂方法:用一个静态方法来对外提供自身实例。(非官方定义)数组

要注意,这里的静态工厂方法跟设计模式中的工厂模式并无什么对应关系。篇幅缘由(主要是懒,这里就不详细说了。微信

静态工厂方法的优点

优点1:有名称。框架

构造器的一个缺点是可能无法对建立的对象有明确的描述,例如构造器BigInteger(int, int, Random)返回的BigInteger可能为素数,可是使用BigInteger probablePrime(int, Random)方法会更加明确。dom

构造器的另外一个缺点是一个类只能有一个带有指定签名的构造器,若是想避开这个限制,能够提供两个构造器,它们的参数列表只是参数类型的顺序不同,但这个会对使用方形成迷惑。静态工厂方法则不受这个限制。ide

优点2:没必要在每次调用它们的时候都建立一个新对象。性能

好比上面的Boolean.valueOf方法,返回值已经预先构建好了,不会新建立对象。同时,日常常用的单例模式,通常也都是经过静态工厂方法实现的。这个比起每次建立一个新对象的成本要低不少。

优点3:能够返回原返回类型的任何子类型的对象。

提供了极大的灵活性,对于面向接口编程很是适用。好比平常的工做中,能够定义方法的返回值为一个接口,在实际返回的时候多是该接口的任意实现类。

另外,还能够返回对象,同时又不会使对象的类变成公有的。

java.util.Collections中有不少这种使用方式:

public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
    return new SetFromMap<>(map);
}

而这个SetFromMap就是一个私有的类

private static class SetFromMap<E> extends AbstractSet<E> implements Set<E>, Serializable{
    ...
}

优点4:所返回的对象的类能够随着每次调用而发生变化,这取决于静态工厂方法的参数值。

这个看文本大概就明白什么意思,跟上一点有点类似,不过更强调的是经过参数值返回不一样的类,有多是基于性能的考虑,也有多是基于业务的考虑。好比根据数组的大小返回不一样的实现。

优点5:在编写包含该方法的类时,返回的对象的类不须要存在。

这是服务提供者框架的基础,典型的应用场景就是JDBC,具体的实现类都是各个数据库驱动包中提供的,编写jdbc相关代码时并不存在。

可能你第一次据说服务提供者框架这个名词,可是你应该早就接触过了。

服务提供者框架是指:多个服务提供者实现一个服务,系统为客户端提供多个实现,并把他们从多个实现中解耦出来。

服务提供者的改变对它们的客户端是透明的,这样提供了更好的可扩展性。例如,JDBC,JMS等就是用了服务提供者框架。

有四个组件

  • Service Interface:服务接口,将服务经过抽象统一声明,供客户端调用、由各个服务提供者具体实现。
  • Provider Registration API:服务提供者注册API,用于系统注册服务提供者,使得客户端能够访问它实现的服务。
  • Service Access API:服务访问API,用户客户端获取相应的服务。
  • Service Provider Interface:服务提供者接口,这些服务提供者负责建立其服务实现的实例。(可选)

看不懂别着急,能够对应到JDBC

  • Service Interface:Connection,客户端的调用都是基于Connection
  • Provider Registration API:DriverManager.registerDriver,注册服务提供者的API,数据库驱动会调用这个API把本身注册。
  • Service Access API:DriverManager.getConnection获取服务的API。
  • Service Provider Interface:Driver,用于建立Connection

这个模式很好用,最近的代码中一直在用这个模式,有兴趣的能够去看看JDBC源码。

静态工厂方法的劣势

劣势1:没有公共或受保护构造方法的类不能被子类化

这个其实还好,若是是本身写的类,有须要的话能够在提供静态工厂方法的同时提供公有的或者受保护的构造器。若是还不行,可使用组合而不是继承,应该知道是啥意思哈,不懂后边也会写(不是这一篇,估计是10篇之后了)。

劣势2:很难找到它们

别笑,这个真实地发生了。我写了一个服务,参数是一个ValueFilter类型的对象,这个类是我本身定义的,而后同事就跟我说你建立这个类须要好多参数,建立起来很麻烦,其实我早就贴心地建好了静态工厂方法,方便别人使用。可是别人在用你的类的时候不必定会看你的方法,建立对象的时候下意识的都是想着构造器。

下面是一些静态工厂方法的惯用名称(照抄

  • from —— 类型转换方法,它只有单个参数,返回该类型的一个相对应的实例,例如:Date d = Date.from(instant);
  • of —— 聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来,例如:Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf —— 比from 和 to 更繁琐的一种替代方式,例如:BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance 或者 getinstance —— 返回的实例是经过方法的(若有)参数来描述的,可是不能说与参数具备相同的值,例如:StackWalker luke = StackWalker.getInstance(options);
  • create 或 newInstance —— 像 instance 或 getInstance 同样,但 create 或者 newInstance 保证每次调用都返回一个新的实例,例如:Object newArray = Array.newInstance(classObject, arrayLen);
  • getType —— 像 getInstance 同样,可是在工厂方法处于不一样的类中的时候使用。Type 表示工厂方法返回的对象类型,例如:FileStore fs = Files.getFileStore(path);
  • newType —— 像 newInstance 同样,可是在工厂方法处于不一样的类中的时候使用。Type表示工厂方法返回的对象类型,例如:BufferedReader br = Files.newBufferedReader(path);
  • type —— getType 和 newType 的简版例如:List litany = Collections.list(legacyLitany);

总结

在须要提供实例的时候不要第一反应就提供公有的构造器,能够优先考虑静态工厂。

参考

看到了这里必定是真爱了,关注微信公众号【憨憨的春天】第一时间获取更新
qrcode_for_gh_7fff61e23381_344.jpg

相关文章
相关标签/搜索