考虑使用静态工厂方法代替构造器(一)

考虑用静态工厂方法代替构造器

优点

它们有名称

  • 指的是方法的名称,能够准确描述返回的对象,对于多个构造方法的类可以使用不同的名称来描述返回对象
  • 对于构造方法参数的含义需要结合注释才能明白用哪一个构造器,并且多构造器的时候无法确切知道使用哪个构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.stardust.effective.role1;

/**
* Created by mike on 2017/12/3.
* 它们有名称
*/
public class Fruit {

private String color;

private String size;

private Fruit(String color,String size){
this.color=color;
this.size=size;
}

/**
* 通过方法名可以知道要构造什么样的对象
* @return
*/
public static Fruit getBigGreenFruit(){
return new Fruit("green","big");
}

public static Fruit getSmallRedFruit(){
return new Fruit("red","small");
}

public static Fruit getApple(){
return new Fruit("red","middle");
}

}

不必每次调用的时候都创建一个新对象

单例模式,对于一些不可变类可以使用这种方式,在上面那个例子中我对构造函数进行了私有化,可以认为是一个简单的单例,但还是需要升级优化,在后面单例的几种写法中展开说。
这种方式的一个优势即不必重复创建对象,在spring中大量使用单例这种设计模式来创建单一不可变对象。
书中还提到一个如果实例受控则可以通过==来判定是否为同一实例,这种写法比equals提升了性能。

术语:
实例受控的类(instance-controlled):在某个时刻只能有某些规定的该类的实例存在。

可以返回原返回类型的任何子类型的对象

文章中是以Collections为例的,一开始没有太理解,Collections是一个工具类,构造私有化的,不能直接实例化,Collection本身是接口,不能有静态工厂方法,所以委托给Collections这个工具类来提供生产构造.
下面贴一段Collection中静态工厂的例子,以List为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* Returns an immutable list containing only the specified object.
* The returned list is serializable.
*
* @param <T> the class of the objects in the list
* @param o the sole object to be stored in the returned list.
* @return an immutable list containing only the specified object.
* @since 1.3
*/
public static <T> List<T> singletonList(T o) {
return new SingletonList<>(o);
}

/**
* @serial include
*/
private static class SingletonList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {

private static final long serialVersionUID = 3093736618740652951L;

private final E element;

SingletonList(E obj) {element = obj;}

public Iterator<E> iterator() {
return singletonIterator(element);
}

public int size() {return 1;}

public boolean contains(Object obj) {return eq(obj, element);}

public E get(int index) {
if (index != 0)
throw new IndexOutOfBoundsException("Index: "+index+", Size: 1");
return element;
}

// Override default methods for Collection
@Override
public void forEach(Consumer<? super E> action) {
action.accept(element);
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {
}
@Override
public Spliterator<E> spliterator() {
return singletonSpliterator(element);
}
}

```

术语:基于接口的框架(interface-based framework):框架的对外访问与接入渠道是基于接口实现的。

#### 在创建参数化类型实例的时候,使代码更加简洁

``` java
// 当前做法
Map<String, List<String>> map new HashMap<String, List<String>>();
// 举例这样做会更简洁
Map<String, List<String>> map = HashMap.newInstance();
// java8已经可以直接这样写了
Map<String, List<String>> map = new HashMap<>();

缺点

类如果不含public或者protected的构造器, 就不能被子类化

  • 例如Collections类,不能被子类化,另一方面就是相当于鼓励复合(Composition),类似于用Collections这类的写法

无法与其他静态方法区分,不像构造方法名称必须为类名,可以使用一些惯用的名称来弥补这一劣势

  • valueOf() : 类型转换方法, 返回的实例与参数具有相同的值.
  • of() : valueOf()的一种更简洁的替代.
  • getInstance(): 返回的实例通过参数来描述, 对于单例来说, 该方法没有参数, 返回唯一的实例.
  • newInstance(): 像getInstance()一样, 但newInstance()能确保返回的每个实例都与其他实例不同.
  • getType() : 像getInstance()一样, Type表示返回的对象类型, 在工厂方法处于不同的类中的时候使用.
  • newType() : 和newInstance()一样, Type表示返回类型, 在工厂方法处于不同的类中的时候使用.
分享到