PS:这本书不适合深究英语的语法,理解或者翻译出作者的意思即可!读书总结而已,内容不一定对,希望能够得到指正
Item 1: Consider static factory methods instead of constructors考虑静态工厂方法代替构造器
The traditional way for a class to allow a client to obtain 获取 an instance is to provide a public constructor.There is another technique 工艺;技术 that should be a part of every programmer's toolkit 工具箱;技能包.A class can provide a public static factory method, which is simply 仅仅是 a static method that returns an instance of the class. Here's a simple example from Boolean (the boxed primitive class for boolean 基本数据类型boolean的包装类). This method translates a boolean primitive value into a Boolean object reference:
public static Boolena valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
- 直接点名主题:除了构造器之外,一个类还可以通过静态工厂方法来提供实例。然后举了Boolean的例子来说明。在Java基本数据类型int、long等的包装类/封装类(Integer、Long等)里面含有大量的静态工厂方法,比如说开发过程中用的比较多的valueOf()方法
- The traditional way for a class to allow a client to obtain an instance is to provide a public constructor,这句的主语是
a class,换一个结构更容易理解一点:To allow a client to obtain an instance,the traditional way for a class is to provide a public constructor.为了能够让客户端获取到类的实例,这个类常用的方式是提供个构造器 - A class can provide a public static factory method, which is simply a static method that returns an instance of the class. 英文版会经常性劝退地出现which引导的从句,后面再跟一个that从句。that从句直接修饰public static factory method就好了,A class can provide a public static factory method that returns an instance of the class:主语跟上面的句子是一样的,都是
a class,表示要让客户端获取到类的实例,这个类可以提供两种方式
Note that a static factory method is not the same as the Factory Method pattern from Design Patterns [Gamma95]. The static factory method described in this item has no direct equivalent 等同 in Design Patterns
A class can provide its clients with static factory methods instead of, or in addition to, public constructors. Providing a static factory method instead of a public constructor has both advantages 好处 and disadvantages 坏处.
特意指出静态工厂方法跟工厂模式是不一样的,下面介绍静态工厂方法的好处跟坏处。到这里可能还有不太清楚静态工厂方法是什么的,其实上面已经说的很清楚了,这里说明一下:
一般来说获取实例最简单的方式就是
new,例如:Date date = new Date(),这就是第一种方式:构造器。另外一种就是静态工厂方法了,就是直接在类里面提供个静态方法对外暴露自身的实例,例如:Integer i = Integer.valueOf("1");LocalDate date = LocalDate.now();这里说一句,静态工厂方法也是一个函数来的,所以说java中函数的特性,它都有。下面说的优点,按我个人理解,其实都是函数的特点来的。
One advantage of static factory methods is that, unlike constructors, they have names. If the parameters to a constructor do not, in and of themselves 他们自己, describe the object being returned, a static factory with a well-chosen 精心挑选的 name is easier to use and the resulting client code easier to read. For example, the constructor BigInteger(int, int, Random), which returns a BigInteger that is probably prime 素数, would have been better expressed as a static factory method named BigInteger.probablePrime. (This method was added in Java 4.)
- 第一个优点是,静态工厂方法可以自己取名,比如说构造函数的参数没有说明将会返回什么样的对象,那么这个时候静态工厂方法就很适合。注意是指构造函数里面逻辑比较复杂的情况下! 作者这里举例了BigInteger里面的一个构造函数,其实你去看看BigInteger源码,可以发现它里面的构造函数逻辑都比较复杂,这时候你就可以理解静态工厂方法拥有名字的重要性了。
A class can have only a single constructor with a given signature 签名. Programmers have been known to get around this restriction 限制 by providing two constructors whose parameter lists differ 不同 only in the order of their parameter types. This is a really bad idea. The user of such an API will never be able to remember which constructor is which and will end up calling the wrong one by mistake. People reading code that uses these constructors will not know what the code does without referring 查阅 to the class documentation.
Because they have names, static factory methods don’t share the restriction 限制 discussed in the previous paragraph 段落. In cases where a class seems to require multiple 多个 constructors with the same signature, replace the constructors with static factory methods and carefully chosen names to highlight 强调 their differences.
PS:还记得我曾经(刚入行不久)在上面这两段中劝退多次,给定签名的构造函数到底是什么?为啥只能有一个?为啥改变参数的顺序可以绕过限制?其实是作者为了说明优点1确实存在而提供的论据,整个小节下来,你会发现作者为了说明静态工厂的优势做了很多阐述、例子或者论据,其实整本书下来都是这样。西方的作者都是这样的,为了说明自己的观点,一定会大篇幅地给出解释,其中包括但不限于数学证明、实验的详细过程。有一些讲解其实可以不看的,这就看读者们自己的判断了!
- 给定签名的构造函数只能有一个,其实就是定义过的构造函数不能重复。比如之前的BigInteger(int, int, Random),定义后就不能重复了,但是面对多个参数并且参数类型不同的时候,可以调换参数的顺序来绕开限制,像BigInteger(int, Random, int)。
A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked 调用. This allows immutable 不可变的 classes (Item 17) to use preconstructed 预先构造的 instances, or to cache instances as they’re constructed, and dispense 分配 them repeatedly to avoid creating unnecessary duplicate objects. The Boolean.valueOf(boolean) method illustrates 阐述 this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create.
- 第二个优点是能够缓存对象的实例,不用每次调用的时候都重复生成,当然这个优点是跟构造函数相比。一般来说,开发过程中缓存实例并对外提供实例都不会用静态方法来暴露,比较常用的方式是通过工厂模式对外提供接口,比如说Spring中单例池的对象。额外说一句,Integer里面就用到了缓存,这个也是面试问的比较多的。
The ability of static factory methods to return the same object from repeated invocations allows classes to maintain 维持;保留 strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. There are several reasons to write instance-controlled classes. Instance control allows a class to guarantee 保证 that it is a singleton (Item 3) or noninstantiable (Item 4). Also, it allows an immutable value class (Item 17) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern 享元模式 [Gamma95]. Enum types (Item 34) provide this guarantee.
- 上面这一段有点不明所以了,大概的意思就是,静态工厂方法每次返回的实例都可以严格控制为同一个对象,基于这一点,可以把这个类变成单例的(具体可以百度单例模式),或者让这个类不可实例化(一般跟单例模式一起使用),亦或者是实现不可变的值类。
- an immutable value class,简单说一下不可变的值类,即指一旦实例化了对象,那么里面的属性值是不可变的。比如说String、BigInteger等,里面的属性值都是用final修饰,是不可变的,源码的注释也写的很清楚了。但是这里说的不可变值类是指通过控制实例返回同一个对象来实现的,因为是同一个对象实例,所以不用加final这样来保证类的不变性。
The ability of static factory methods to return the same object from repeated invocationsallows classes to maintain strict control over what instances exist at any time. 这句话比较难,主语是静态工厂方法能够在反复调用下返回相同对象的能力,后面的at any time修饰的时classes,那么classes to maintain strict control over what instances exist at any time可以翻译成:类能够在任何时候对存在的实例保持严格的控制。最后翻译成:静态工厂方法能够在反复调用下返回相同对象的能力,可以让类在任何时候对存在的实例保持严格的控制。
A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type. This gives you great flexibility 灵活性 in choosing the class of the returned object.
One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks (Item 20), where interfaces provide natural return types for static factory methods.
- 第三个优点,其实是java的高阶应用?因为构造函数不能用泛型等,所以java延伸出来的特性也算作是优点。优点3:静态工厂方法返回的类型,可以是当前返回类型的子类。下面举了Collections的例子
Prior to Java 8, interfaces couldn’t have static methods. By convention, static factory methods for an interface named Type were put in a noninstantiable companion class 伴生类 (Item 4) named Types. For example, the Java Collections Framework has forty-five utility implementations of its interfaces, providing unmodifiable 不可修改 collections, synchronized 同步的 collections, and the like. Nearly all of these implementations are exported 输出 via 通过 static factory methods in one noninstantiable class (java.util.Collections). The classes of the returned objects are all nonpublic.
The Collections Framework API is much smaller than it would have been had it exported forty-five separate public classes, one for each convenience implementation. It is not just the bulk 体量;体积 of the API that is reduced 减少 but the conceptual weight: the number and difficulty of the concepts 观念 that programmers must master 掌握 in order to use the API. The programmer knows that the returned object has precisely [precise:精确的] the API specified by its interface, so there is no need to read additional class documentation for the implementation class. Furthermore, using such a static factory method requires the client to refer to the returned object by interface rather than implementation class, which is generally good practice (Item 64).
- 上面两段,举例说明优点3的好处。Java8之前,接口里面不能写的方法,所以一般通过伴生类来实现一些通用的代码。伴生类,作者也解释了,如果一个接口叫
Type,如Collection接口,那么它的伴生类就是Typs,也就是说Collection接口的伴生类就是Collections,并且伴生类是不可实例化的。 - For example, the Java Collections Framework has forty-five utility implementations of its interfaces, providing unmodifiable collections, synchronized collections, and the like. 比如说:Java的集合框架,里面包含45个Collection接口的实现类,诸如unmodifiable collections(unmodifiable的List、Set等),synchronized collections(synchronized的List、Set等)。
- The Collections Framework API is much smaller than it would have been had it exported forty-five separate public classes, one for each convenience implementation. 这句话也是我自己乱翻的,看看就好。Collections Framework API比什么更小? it would have been had,其实是it would have been + 过去分词的句型,虚拟语气,这里翻译成:可能会出现的那样。it would have been had (that) it exported forty-five separate public classes, one for each convenience implementation. 我感觉是省略了个that:可能会写成45个独立的公共类,每个公共类都是一个实现类。整句翻译:Collections Framework API比写成45个独立的实现类要小的多。
- 来看看Collections里面的部分代码
public class Collections {
//私有构造器,不能通过new关键字实例化
private Collections() {}
... ...
public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
return new UnmodifiableSet<>(s);
}
static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
implements Set<E>, Serializable {
private static final long serialVersionUID = -9215047833775013803L;
UnmodifiableSet(Set<? extends E> s) {super(s);}
public boolean equals(Object o) {return o == this || c.equals(o);}
public int hashCode() {return c.hashCode();}
}
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s) {
return new UnmodifiableSortedSet<>(s);
}
...
static <T> Set<T> synchronizedSet(Set<T> s, Object mutex) {
return new SynchronizedSet<>(s, mutex);
}
static class SynchronizedSet<E>
extends SynchronizedCollection<E>
implements Set<E> {
private static final long serialVersionUID = 487447009682186044L;
SynchronizedSet(Set<E> s) {
super(s);
}
SynchronizedSet(Set<E> s, Object mutex) {
super(s, mutex);
}
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return c.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return c.hashCode();}
}
}
...
}
- 里面有大量的内部类,包含了各种Collection接口的扩展,如unmodifiable的Set、List等, synchronized的Set、List等。因为Collections的构造函数私有的,所以这些内部类的实例化只能通过类的静态(工厂)方法。
- 按我的理解,这只是泛型+内部类的使用技巧,这个优势基本上体现了java的高阶特性。真想要研究透彻的话,可以去研究泛型,不过Collections的设计还真是巧妙。提个有意思的问题:fastjson是如何处理泛型的?
- 伴生类的设计精妙,作者也说了。假如每个接口实现都写一个public class的话,那么45个接口的实现就要多加45个单独的class。而使用静态工厂方法来实现,不仅仅是代码体量上的减少,而且理解API的成本也减少了,因为静态工厂方法里面指定了具体类型的参数,不需要去查阅相关的接口文档。如
public static <T> Set<T> unmodifiableSet(Set<? extends T> s),入参和出参都指明了是Set。但我还是感觉这些优势其实是java语言的特性,这里只是在静态方法里面的使用技巧,不过设计确实很精妙。
As of Java 8, the restriction that interfaces cannot contain static methods was eliminated 被排除的;被淘汰的, so there is typically little reason to provide a noninstantiable companion class for an interface. Many public static members that would have been at home in such a class should instead be put in the interface itself. Note, however, that it may still be necessary to put the bulk of the implementation code behind these static methods in a separate package-private class. This is because Java 8 requires all static members of an interface to be public. Java 9 allows private static methods, but static fields and static member classes are still required to be public.
- 自从java8开始,接口里面也能写静态方法了,可以不用伴生类的形式来提供接口的实现,直接在接口里面实现代码即可。这一段了解一下即可!
A fourth advantage of static factories is that the class of the returned object can vary 不同于 from call to call as a function of the input parameters. Any subtype 子类型 of the declared return type is permissible. The class of the returned object can also vary from release to release.
The EnumSet class (Item 36) has no public constructors, only static factories. In the OpenJDK implementation, they return an instance of one of two subclasses, depending on the size of the underlying 底层的,潜在的 enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.
The existence of these two implementation classes is invisible 不可见的 to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated 被排除的;被淘汰的 from a future release with no ill effects 效果;影响. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved 发现;证实 beneficial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet.
- 如果去看EnumSet的代码的话,优点4,在我看来,也可以算作是泛型+继承的优势,甚至可以当成工厂模式的特点。优点4:静态工厂方法返回的对象实例,可能会根据入参的不同而不同。
- 后面两段用EnumSet解释了这一优势,主要讲了EnumSet的实现细节。静态工厂方法中返回的实例是EnumSet,但是具体的返回类型是EnumSet的子类。客户端只需知道返回EnumSet,代码里面的实现细节对客户端是无感知的。如果你换成工厂模式,其实没什么区别,返回类型是父类,通过参数的不同,返回不同的子类,子类的多少随便你扩展。下面是EnumSet的关键代码和工厂模式的部分代码:
/**
* EnumSet
*/
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");
//根据Enum的长度,使用不同子类的实现
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
/**
* 工厂模式,把水果类换成EnumSet,你会发现代码没什么区别
*/
public class fruitFactory {
public static Fruit createFruit(Integer type) {
Fruit fruit;
switch (type) {
case 1:
//Apple是Furit的子类
fruit = new Apple();
break;
default:
throw new IllegalArgumentException("我们没这种水果!");
}
return fruit;
}
}
A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, like the Java Database Connectivity API (JDBC). A service provider framework is a system in which providers implement a service, and the system makes the implementations available 有用的;可获得的 to clients, decoupling 解耦 the clients from the implementations.
There are three essential 必要的 components in a service provider framework: a service interface, which represents 表示;代表 an implementation; a provider registration 登记;注册 API, which providers use to register 注册 implementations; and a service access 访问;进入 API, which clients use to obtain instances of the service. The service access API may allow clients to specify 指定;规定 criteria 标准;原则 for choosing an implementation. In the absence 缺席;缺乏 of such criteria, the API returns an instance of a default implementation, or allows the client to cycle through all available implementations. The service access API is the flexible static factory that forms 形成;构成 the basis of the service provider framework.
An optional fourth component of a service provider framework is a service provider interface, which describes a factory object that produce instances of the service interface. In the absence of a service provider interface, implementations must be instantiated 实例化 reflectively 反射地 (Item 65). In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface.
There are many variants 变型;变种 of the service provider framework pattern. For example, the service access API can return a richer service interface to clients than the one furnished 供应;提供 by providers. This is the Bridge pattern [Gamma95]. Dependency injection 注入 frameworks (Item 5) can be viewed as powerful service providers. Since Java 6, the platform includes a general-purpose service provider framework, java.util.ServiceLoader, so you needn’t, and generally shouldn’t, write your own (Item 59). JDBC doesn’t use ServiceLoader, as the former predates 先与;早于 the latter.
- 第五个优势:编写包含该方法的类时,返回对象的类不需要存在。刚开始看可能会很懵逼,作者用JDBC框架来做解释,具体就不展开了,可以百度一下JDBC的实现机制。这个优点了解一下就好了,真要展开就太多内容了。
- 如果有兴趣去研究的话,那么可以先去了解spi机制,再去网上查阅一下作者提到的java中使用
java.util.ServiceLoader实现的spi机制。作者也说了,JDBC早于java的spi,那么JDBC又是怎么实现服务提供的,可以网上或者源码上看看实现的原理。最后的扩展,了解dubbo和spring中spi的实现原理,springboot的自动装备就是用spring的spi实现的。
The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed. For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise because it encourages programmers to use composition 组合 instead of inheritance 继承 (Item 18), and is required for immutable types (Item 17).
- 接着说了缺点,缺点1:如果类里面是私有的构造函数(不是public或者protected的构造函数),那么是不能通过子类继承它来进行扩展的。下面是缺点2还有作者的一些建议,比较简单。
A second shortcoming of static factory methods is that they are hard for programmers to find. They do not stand out 突出;引人注目 in API documentation in the way that constructors do, so it can be difficult to figure out 想出;断定 how to instantiate a class that provides static factory methods instead of constructors. The Javadoc tool may someday draw attention to 引起...的注意 static factory methods. In the meantime, you can reduce this problem by drawing attention to static factories in class or interface documentation and by adhering to 坚持;遵循 common naming conventions. Here are some common names for static factory methods. This list is far from exhaustive 详尽的;全面的:
- from—A type-conversion method that takes a single parameter and returns a
corresponding 相应的;一致的instance of this type, for example: Date d = Date.from(instant); - of—An
aggregation 聚合;集合method that takes multiple parameters and returns an instance of this type thatincorporates 合并吸收them, for example: Set faceCards = EnumSet.of(JACK, QUEEN, KING); - valueOf—A more
verbose 冗长的;啰唆的alternative 替代的;另类的to from and of, for example: BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE); - instance or getInstance—Returns an instance that is described by its parameters (if any) but cannot be said to have the same value, for example: StackWalker luke = StackWalker.getInstance(options);
- create or newInstance—Like instance or getInstance, except that the method guarantees that each call returns a new instance, for example: Object newArray = Array.newInstance(classObject, arrayLen);
- getType—Like getInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example: FileStore fs = Files.getFileStore(path);
- newType—Like newInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example: BufferedReader br = Files.newBufferedReader(path);
- type—A
concise 简明的alternative 替代的;另类的to getType and newType, for example: List litany = Collections.list(legacyLitany);
In summary, static factory methods and public constructors both have their uses, and it pays to understand their relative 相应的;相关的 merits 优点;价值. Often static factories are preferable 更合适, so avoid the reflex 反射 to provide public constructors without first considering static factories.
- 总结,这一小节,在实际开发中用处不大,知道还有这样的技巧即可,除了第一个优点之外,其他几个优点我认为都是根据java特性而产生的编程技巧。当然,对于构造器,这些优点都还挺明显的。一家之言,按需阅读!