第二章 创建和销毁对象
Item 1:使用静态工厂方法替代构造函数
优点
- 静态工厂方法可以通过命名表达出作者的意图,使得代码更清晰,更易读;
- 静态工厂方法不需要每次都创建一个新对象(对比构造函数)。从而,为提升代码效率提供了可能性;
- 静态工厂方法可以返回任意的派生对象。这一点类似于设计模式中的“工厂方法”,但细节不尽相同。从 Java 8 开始,接口已经支持静态方法,为此方式提供更大的便利;
- 以不同的参数调用同一静态工厂方法可以返回不同的对象。
- 这一点也是从性能方面考虑。譬如,EnumSet 当枚举数量小于等于 64 个时,返回基于 long 底层数据的对象;反之,则底层数据基于 long 数组来构建;
- 另外一点是,随着版本更新,底层库可以更新内部实现而不影响外部调用;
- 当静态工厂方法被实现的时候,返回对象甚至还可以没有被设计出来,还不存在;就像 JDBC(Java Database Connectivity) 库一样,使用的是服务提供方框架,具体的服务提供方可以在运行过程中注册进来。这个方式达成了双向解耦的目标;
缺陷
- 类没有公共或保护的构造函数,没法被子类化。现在的编程范式鼓励多使用复合,而不是继承,因此这一点不算大毛病
- 开发者难以探究静态工厂方法的真正用法,这个一个是可以通过改进 javadoc 工具来解决,另外通过一些通过的命名规范来减轻。典型的一些命名规范如下:
- from - 类型转换
Date d = Date.from(instant);
- of - 聚合多个参数,返回指定对象
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf - 类型转换
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance 或者 instanceOf - 返回指定参数的实例,当不保证实例是唯一的(即 Singleton 模式)
StackWalker luke = StackWalker.getInstance(options);
- create 或者 newInstance - 每次都返回一个新对象,不复用
Object newArray = Array.newInstance(classObject, arrayLen);
- getType - 获取指定类型,通过用于 返回类型和静态工厂方法不在同一个类的情况下。
FileStore fs = Files.getFileStore(path);
- newType - 创建指定类型,通过用于 返回类型和静态工厂方法不在同一个类的情况下。
BufferedReader br = Files.newBufferedReader(path);
- type - 获得指定类型(可替代 getType 和 newType)。
List<Complaint> litany = Collections.list(legacyLitany);
思考
记得早年学习的时候,听到过这么一句话:“All problem in computer science can be solved by anther layer of indirection.” (“计算机科学领域的所有问题都可以通过增加一个间接的中间层来解决” )。工作越久,越觉得是至理名言呀。