考虑用静态工厂方法代替构造器
什么是静态工厂方法
在 Java 中,获得一个类实例最简单的方法就是使用 new 关键字,通过构造函数来实现对象的创建。
就像这样:
Fragment fragment = new MyFragment();
// or
Date date = new Date();
不过在实际的开发中,我们经常还会见到另外一种获取类实例的方法:
Fragment fragment = MyFragment.newIntance();
// or
Calendar calendar = Calendar.getInstance();
// or
Integer number = Integer.valueOf("3");
↑ 像这样的:不通过 new,而是用一个静态方法来对外提供自身实例的方法,即为我们所说的静态工厂方法(Static factory method)。
Effective Java 对于静态工厂总结优势
1.静态工厂方法与构造器不同的第一优势在于,它们有名字
2.静态工厂方法与构造器不同的第二个优势,不用每次被调用时都创建新对象
3.静态工厂方法与构造器不同的第三个优势,可以返回原返回类型的子类
4.第四个优势,在创建带泛型的实例时,能使代码变得简洁
静态工厂方法可以具有参数相同但名称不同的方法(这区别于构成器只能是统一名称)
class Child{
int age = 10;
int weight = 30;
public static Child newChild(int age, int weight) {
Child child = new Child();
child.weight = weight;
child.age = age;
return child;
}
public static Child newChildWithWeight(int weight) {
Child child = new Child();
child.weight = weight;
return child;
}
public static Child newChildWithAge(int age) {
Child child = new Child();
child.age = age;
return child;
}
}
↑ 像这样的:区别与构造器只能是同一名称的问题,newChild(),newChildWithWeight(),newChildWithAge(),显示的更加清楚,见名知意
(注意:慎重的选择名称以便突出它们之间的区别)。
通过静态工厂方法减少对外暴露的属性
软件开发中有一条很重要的经验:对外暴露的属性越多,调用者就越容易出错。所以对于类的提供者,一般来说,应该努力减少对外暴露属性,从而降低调用者出错的机会。
考虑一下有如下一个 Player 类:
// Player : Version 1
class Player {
public static final int TYPE_RUNNER = 1;
public static final int TYPE_SWIMMER = 2;
public static final int TYPE_RACER = 3;
protected int type;
public Player(int type) {
this.type = type;
}
}
Player 对外提供了一个构造方法,让使用者传入一个 type 来表示类型。那么这个类期望的调用方式就是这样的:
Player player1 = new Player(Player.TYPE_RUNNER);
Player player2 = new Player(Player.TYPE_SWEIMMER);
但是,我们知道,提供者是无法控制调用方的行为的,实际中调用方式可能是这样的:
Player player3 = new Player(0);
Player player4 = new Player(-1);
Player player5 = new Player(10086);
提供者期望的构造函数传入的值是事先定义好的几个常量之一,但如果不是,就很容易导致程序错误。
—— 要避免这种错误,使用枚举来代替常量值是常见的方法之一,当然如果不想用枚举的话,使用我们今天所说的主角静态工厂方法也是一个很好的办法。
如果把以上需求用静态工厂方法来实现,代码大致是这样的:
// Player : Version 2
class Player {
public static final int TYPE_RUNNER = 1;
public static final int TYPE_SWIMMER = 2;
public static final int TYPE_RACER = 3;
int type;
private Player(int type) {
this.type = type;
}
public static Player newRunner() {
return new Player(TYPE_RUNNER);
}
public static Player newSwimmer() {
return new Player(TYPE_SWIMMER);
}
public static Player newRacer() {
return new Player(TYPE_RACER);
}
}
注意其中的构造方法被声明为了 private,这样可以防止它被外部调用,于是调用方在使用 Player 实例的时候,基本上就必须通过 newRunner、newSwimmer、newRacer 这几个静态工厂方法来创建,调用方无须知道也无须指定 type 值 —— 这样就能把 type 的赋值的范围控制住,防止前面所说的异常值的情况