书的第一章是创建和销毁对象,接下来的几篇也都是围绕这个展开。
本篇对应书中的第一条:用静态工厂方法代替构造器。
先看一个例子,Boolean类中有如下构造器
public Boolean(boolean value) { this.value = value; }
同时,还提供了如下的静态方法,也可以返回Boolean类实例
public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
这个就是静态工厂方法:用一个静态方法来对外提供自身实例。(非官方定义)
要注意,这里的静态工厂方法跟设计模式中的工厂模式并没有什么对应关系。篇幅原因(主要是懒,这里就不详细说了。
构造器的一个缺点是可能没法对创建的对象有明确的描述,例如构造器 BigInteger(int, int, Random)
返回的BigInteger可能为素数,但是使用 BigInteger probablePrime(int, Random)
方法会更加明确。
构造器的另一个缺点是一个类只能有一个带有指定签名的构造器,如果想避开这个限制,可以提供两个构造器,它们的参数列表只是参数类型的顺序不一样,但这个会对使用方造成迷惑。静态工厂方法则不受这个限制。
比如上面的 Boolean.valueOf
方法,返回值已经预先构建好了,不会新创建对象。同时,平常经常使用的单例模式,一般也都是通过静态工厂方法实现的。这个比起每次创建一个新对象的成本要低很多。
提供了极大的灵活性,对于面向接口编程非常适用。比如日常的工作中,可以定义方法的返回值为一个接口,在实际返回的时候可能是该接口的任意实现类。
另外,还可以返回对象,同时又不会使对象的类变成公有的。
在 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{ ... }
这个看文本大概就明白什么意思,跟上一点有点相似,不过更强调的是通过参数值返回不同的类,有可能是基于性能的考虑,也有可能是基于业务的考虑。比如根据数组的大小返回不同的实现。
这是 服务提供者框架 的基础,典型的应用场景就是JDBC,具体的实现类都是各个数据库驱动包中提供的,编写jdbc相关代码时并不存在。
可能你第一次听说服务提供者框架这个名词,但是你应该早就接触过了。
服务提供者框架是指:多个服务提供者实现一个服务,系统为客户端提供多个实现,并把他们从多个实现中解耦出来。
服务提供者的改变对它们的客户端是透明的,这样提供了更好的可扩展性。例如,JDBC,JMS等就是用了服务提供者框架。
有四个组件
看不懂别着急,可以对应到JDBC
Connection
,客户端的调用都是基于 Connection
。 DriverManager.registerDriver
,注册服务提供者的API,数据库驱动会调用这个API把自己注册。 DriverManager.getConnection
获取服务的API。 Driver
,用于创建 Connection
。 这个模式很好用,最近的代码中一直在用这个模式,有兴趣的可以去看看JDBC源码。
这个其实还好,如果是自己写的类,有需要的话可以在提供静态工厂方法的同时提供公有的或者受保护的构造器。如果还不行,可以使用组合而不是继承,应该知道是啥意思哈,不懂后边也会写(不是这一篇,估计是10篇以后了)。
别笑,这个真实地发生了。我写了一个服务,参数是一个 ValueFilter
类型的对象,这个类是我自己定义的,然后同事就跟我说你创建这个类需要好多参数,创建起来很麻烦,其实我早就贴心地建好了静态工厂方法,方便别人使用。但是别人在用你的类的时候不一定会看你的方法,创建对象的时候下意识的都是想着构造器。
下面是一些静态工厂方法的惯用名称(照抄
Date d = Date.from(instant); Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING); BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE); StackWalker luke = StackWalker.getInstance(options); Object newArray = Array.newInstance(classObject, arrayLen); FileStore fs = Files.getFileStore(path); BufferedReader br = Files.newBufferedReader(path); List litany = Collections.list(legacyLitany);
在需要提供实例的时候不要第一反应就提供公有的构造器,可以优先考虑静态工厂。
看到了这里一定是真爱了,关注微信公众号【憨憨的春天】第一时间获取更新