第01条 用静态工厂方法代替构造方法

218 阅读3分钟

获取类的实例

对于一个类来说,让使用着获取它自身的一个实例,最常用的方法是提供一个公有的构造方法

// 以包装类Boolean为例
Boolean flag = new Boolean("true");

public Boolean(String s){
    this(toBoolean(s));
}

除了公有构造方法外,我们更应该提供一个静态工厂方法,它只是一个返回该类实例的静态方法。

Boolean flag = Boolean.valueOf(true);

public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE : Boolean.FALSE ;
}

静态工厂方法与工厂方法模式区别

  • 静态工厂方法指某个类里的静态方法,通过调用该静态方法可以得到属于该类的一个实例
  • 工厂方法模式是一种设计模式,指的是让具体的工厂对象负责生产具体的产品对象,这里涉及多种工厂(类),多种对象(类),如:内存工厂生产内存对象,CPU工厂生产CPU对象
  • 简单工厂模式里的静态工厂方法会创建各种不同的对象(不同类的实例),而静态工厂方法一般只创建属于该类的一个实例(包括子类)

静态工厂方法的优势

优势1:可读性强,方法有名称
  • 假设我们需要写一个产生随即数的类RandomIntGenerator,该类有两个成员属性:最小值min和最大值max,假设我们的需求是需要创建三种类型的RandomIntGenerator对象,

    • 大于min,小于max;
    • 大于min 小于Integer.MAX_VALUE;
    • 大于Integer.MIN_VALUE 小于max
  • 如果我们不使用静态工厂方法,代码一般如下设计:

    • 同样的构造方法,不看注释不查代码就很难明白对象创建的具体含义
public class RandomIntGenerator{

    // 最小值
    private int min = Integer.MIN_VALUE;
    
    // 最大值
    private int max = Integer.MAX_VALUE;

    // 大于min,小于max
    public RandomIntGenerator(int min, int max){
        this.min = min;
        this.max = max;
    }
    
    
    // 大于min 小于Integer.MAX_VALUE
    public RandomIntGenerator(int min){
        this.min = min;
    }

    // 大于Integer.MIN_VALUE 小于max
    // 报错:Duplicate method RandomIntGenerator(int) in type RandomIntGenerator
    // 原因:已经存在一个参数一致的工作方法,重复定义了
    public RandomIntGenerator(int max){
        this.max = max;
    }
}
  • 使用静态工厂方法实现
public class RandomIntGenerator {

    private int max = Integer.MAX_VALUE;
    private int min = Integer.MIN_VALUE;

    private RandomIntGenerator(int min, int max) {
        this.min = min;
        this.max = max;
    }

    // 通过名字就可以知道
    public static RandomIntGenerator between(int min, int max) {
        return new RandomIntGenerator(min, max);
    }

    public static RandomIntGenerator biggerThan(int min) {
        return new RandomIntGenerator(min, Integer.MAX_VALUE);
    }

    public static RandomIntGenerator smallerThan(int max) {
        return new RandomIntGenerator(Integer.MIN_VALUE, max);
    }
}
优势2:调用的时候不用每次都创建新对象
  • JDK中的Boolean类的valueOf方法可以很好的印证这个优势,在Boolean类中,有两个事先创建好的Boolean对象(True,False)
  • 当我们调用Boolean.valueOf("true")方法时,返回的就是这两个实例的引用,这样可以避免创建不必要的对象
public final class Boolean implements Serializable,Comparable<Boolean>{

    public static final Boolean TRUE = new Boolean(true);

    public static final Boolean FALSE = new Boolean(false);
}


public static Boolean valueOf(String s) {
    return toBoolean(s) ? TRUE : FALSE;
}
优势3:可以返回原返回类型的任何子类型对象
//RedDog和BlackDog为Dog的子类
public static Dog getInstance(){
    return new RedDog();//或者return new BlackDog();
}
优势4:代码更加简洁
class MyMap<K,V> {
    
    public MyMap(){

    }

    public static <K,V> MyMap<K,V> newInstance(){
        return new MyMap<K, V>();
    }
}

public class Main{
    public static void main(String[] args){
        MyMap<String, String> map1 = new MyMap<String, String>();

        // 更加简洁,不需要重复指明类型参数,可以自行推导出来
        MyMap<String, String> map2 = MyMap.newInstance();
    }

}

使用静态工厂方法的缺点

如果类不含public或protect的构造方法,将不能被继承
// 此类因为没有public或protect的构造,故不能被继承
class MyMap<K,V> {
  
    private MyMap(){

    }

    public static <K,V> MyMap<K,V> newInstance(){
        return new MyMap<K, V>();
    }
}
与其它普通静态方法没有区别,没有明确的标识一个静态方法用于实例化类
  • 所以,一般一个静态工厂方法需要有详细的注释,遵守标准的命名,如使用getInstance、valueOf、newInstance等方法名