第1条 用静态方法代替构造器
静态工厂方法与设计模式中的工厂方法模式不同
静态工厂方法的优势
-
静态工厂方法有名称,可以更确切地描述正被返回的对象。这是比较重要的优势之一了,在编码的过程中,名字的好坏往往会决定代码的可读性。比如getInstance和newInstance对比起来,可以很明显的感觉到前者是单例,后者是多例的
-
不必在每次调用它们的时候都创建一个新对象。可以在类内部构造几个常用的单例常量,可以在一定程度上节约内存。比如
Boolean.valueOf
-
可以返回原类型的子类型对象。这一点也是比较重要的功能了,不只是子类,如果构造方法里有参数的时候,也可以在静态方法中给这个参数赋上不同的值
public class ValueParser<T> { private final Function<T, String> parser; private ValueParser(Function<T, String> parser) { this.parser = parser; } public static ValueParser<Boolean> bool() { return new ValueParser<>(String::valueOf); } public static ValueParser<String> string() { return new ValueParser<>(e -> e); } }
-
返回对象的类型可以根据输入的参数而变化。比如说OpenJdk的EnumSet,它的构造方法是默认类型的,noneOf的实现会根据具体的数量返回不同的类型。和上面第3点有些许重合
EnumSet(Class<E>elementType, Enum<?>[] universe) { this.elementType = elementType; this.universe = universe; } public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }
-
返回对象的类型不需要在写这个方法的时候就存在。这个地方主要还是依赖反射。比如JDBC,在编写一系列框架的时候,并没有Driver的实现类,那就需要通过反射创建假设自己有了。比较经典的就是ServiceLoader。这个特性主要用在底层框架中,业务代码中很少会用到
静态工厂方法的缺点
-
类如果不含public或者protected的构造器, 就不能被子类化。静态工厂方法构造基本都是要把构造器私有化的。这条缺点其实也会变相的鼓励程序员多使用组合
-
不容易被程序员发现。书中提到的是因为这类方法不会像构造器一样在API文档中表明出来。我觉得这一点不太成立,反而书中的内容更像是规范大家命名。因为在实际使用中,我们自己写的类不会生成Java doc,而别人想要使用一定会打开这个类并且查看别人的使用方法。
书中还列举了常见的几个命名:
-
from
: 类型转换方法。Date d = Date.from(instant);
-
of
: 聚集方法, 参数为多个, 返回的当前类型的实例包含了它们。Set<Ranl> cards = EnumSet.of(JACK, QUEEN, KING);
-
valueOf
: 类型转换方法, 返回的实例与参数具有相同的值。BigInteger.valueOf(Integer.MAX_VALUE);
-
instance
或getInstance
: 返回的实例通过参数来描述(并不是和参数有一样的值)..对于单例来说, 该方法没有参数, 返回唯一的实例。StackWalker luke = StackWalker.getInstance(options);
-
create
或newInstance
: 像getInstance
一样, 但newInstance
能确保返回的每个实例都与其他实例不同。Object array = Array.newInstance(classObject, arrayLen);
-
getType
: 和getInstance
一样, Type表示返回的对象类型, 在工厂方法处于不同的类中的时候使用。FileStore fs = Files.getFileStore(path);
-
newType
: 和newInstance
一样, Type表示返回的对象类型, 在工厂方法处于不同的类中的时候使用。BufferedReader br = Files.newBufferedReader(path);
-
type
:getType
和newType
的简洁替代。List<Complaint> litany = Collections.list(legacyLitany);
总结一下其实大概3中情况:
- 类型转换:of/from
- 单例多例构造方法:getInstance/newInstance
- 返回子类:newType
-
对于TMS来说,我们通常写的helper、wrapper、cache(cache基本是一定要用的)都可以使用这种构造方法。目前我看过的代码里,SaveTariff可以通过工厂进行改造,后面隔周分享的时候拿出来讨论下
网友评论