首页 > 代码库 > 第1条:考虑用静态工厂方法代替构造器

第1条:考虑用静态工厂方法代替构造器

   首先我们先看下一个类RandomIntBuilder,用来创建随机的Int整数

public class RandomIntBuilder {
    private final int min;
    private final int max;

    public int next() {...}
}

  接下来我们来加入几个构造器来看看

//没任何问题
public RandomIntBuilder(int min, int max){
      this.min = min;
      this.max = max;
}

//没任何问题
//产生一个介于min到Integer.MAX_VALUE的值
public RandomIntBuilder(int min){
      this.min = min;
      this.max = Integer.MAX_VALUE;
}

//编译错误Duplicate method RandomIntBuilder(int) in type RandomIntBuilder
//产生一个介于Integer.MIN_VALUE到max的值
public RandomIntBuilder(int max){
      this.min = Integer.MIN_VALUE;
      this.max = max;
}

  从上面很明显可以看出,因为构造器区别在于参数类型、个数、顺序不同而已,上面的第三个和第二个构造方法并没有这些不同,因此无法区别才导致报错。这时候,我们幸好有静态工厂方法,我们可以通过使用简单的公共静态方法返回一个类的实例。在Java中,你一定见过Boolean.valueOf吧,就像下面这样:

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

  因此,我们将静态工厂方法用在这里,上面的类就可以变成如下写法:

public class RandomIntBuilder {
    private final int min;
    private final int max;

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

    public static RandomIntBuilderbetween(int max, int min) {
        return new RandomIntBuilder (min, max);
    }

    public static RandomIntBuilderForMin(int min) {
        return new RandomIntBuilder (min, Integer.MAX_VALUE);
    }

    public static RandomIntBuilderForMax(int max) {
        return new RandomIntBuilder (Integer.MIN_VALUE, max);
    }

    public int next() {...}
}

  同时,使用静态工厂方法还可以让返回对象的类型可以是返回类型的任意子类型。这使你随意更改返回类型而不用担心影响到客户端代码成为了可能。通过下面的一个例子来说明。我们先定义一个接口,这个接口可以产生其他数据类型如String, Double或者Long的随机生成器

public interface RandomBuilder<T> {
  T next();
}

  下面我们来编写下Integer和String的生成器

class RandomIntBuilder implements RandomBuilder<Integer> {
    private final int min;
    private final int max;

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

    public Integer next() {...}
}
class RandomStringBuilder implements RandomBuilder<String> {
    private final String prefix;

    RandomStringBuilder (String prefix) {
        this.prefix = prefix;
    }

    public String next() {...}
}

  可以发现,上面的所有的类及类的构造器被定义为包权限。也就是说除了本包之外的客户端代码无法创建这些生成器的实例,那么该怎么办呢?其实我们只需要提供一个给外面的公共类就可以了

public final class RandomBuilderCreator {

    private RandomBuilderCreator () {}

    public static final RandomBuilder<Integer> getIntBuilder() {
     //返回了子类型
        return new RandomIntBuilder(Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    public static final RandomBuilder<String> getStringBuilder() {
        //返回了子类型  
        return new RandomStringBuilder("");
    }
}

  好了,这就静态工厂方法代替构造器的几点好处了,现在让我们总结下静态工厂方法和构造器的优劣对比

  优势:

    ①静态工厂方法与构造器不同的第一大优势在于它们有名称,能更好理解意思。

    ②静态工厂方法与构造器不同的第二大优势在于不必在每次调用它们的时候都创建一个新对象。就像Boolean,我们不需要创建Boolean对象来获取b的值,而只要调用Boolean.valueOf(b)即可。

    ③静态工厂方法与构造器不同的第三大优势在于它们可以返回原返回类型的任意子类型对象。

    ④静态工厂方法与构造器不同的第三大优势在于创建参数化实例的时候可以使代码变得更加简洁。就像下面的代码

      Map<String, List<String>> map = new HashMap<String, List<String>>();

      这显得很繁琐,如果HashMap有了以下静态工厂方法,java会进行类型推导,将代码简洁化了为:Map<String, List<String>> map = HashMap.newInstance();

      public static <K,V> HashMap<K,V> new newInstance(){

        return new HashMap<K,V>();
      }

  劣势:

    ①静态工厂方法的主要缺点在于类如果不含有公有的或者受保护的构造器,就不能被子类化了。

    ②静态工厂方法的第二个缺点是他们与其他的静态方法实际上没有任何区别。因此在API中没有像构造方法那样呗明确标识出来,找起来会比较困难繁琐。

  

第1条:考虑用静态工厂方法代替构造器