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

引言

我们写了一个类,为了让该类之外的代码中能拿到它的一个实例,最常见的做法是提供一个公有的构造器。另一种做法是提供一个公有的静态工厂方法。它只是一个静态方法,返回该类的实例。下面是示例,这个方法将boolean的基本类型值转换成了一个Boolean对象:

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

注意:这里说的静态工厂方法与设计模式中的工厂方法模式不同,不直接对应于设计模式中的工厂方法。

下面来谈一趟,用静态工厂方法代替构造器的优势与弊端。

用静态工厂方法代替构造器的第一个优势在于,静态工厂方法有名称

如果构造器的参数本身没有确切地描述返回的对象,那么具有适当名称的静态工厂方法会更容易使用,代码更容易阅读。例如,构造器BigInteger(int ,int, Random) 返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法来表示,显得更为清楚。

我们经常会进行构造器重载,即提供多个构造器,他们只是参数类型顺序不一样。这并不是个好主意,因为常常会调用错误的构造器,记不住什么时候该用哪个。

静态工厂方法有名称,当一个类需要多个构造器时,用静态工厂方法代替构造器,并且选择合适名称以便突出他们的区别

用静态工厂方法代替构造器的第二大优势在于,不必再每次调用他们时都创建一个新对象

静态工厂方法能够为重复的调用返回相同对象,例如,单例模式。

用静态工厂方法代替构造器的第三大优势在于,他们可以返回原返回类型的任何子类型的对象

例如,Java Collections Framework的集合接口,分别提供了不可修改集合、同步集合等等。java.util.Collections中很多的静态方法就是干这事的,如(Collections.synchronizedList):

    /**
     * Returns a synchronized (thread-safe) list backed by the specified
     * list.  In order to guarantee serial access, it is critical that
     * <strong>all</strong> access to the backing list is accomplished
     * through the returned list.<p>
     *
     * It is imperative that the user manually synchronize on the returned
     * list when iterating over it:
     * <pre>
     *  List list = Collections.synchronizedList(new ArrayList());
     *      ...
     *  synchronized (list) {
     *      Iterator i = list.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * </pre>
     * Failure to follow this advice may result in non-deterministic behavior.
     *
     * <p>The returned list will be serializable if the specified list is
     * serializable.
     *
     * @param  <T> the class of the objects in the list
     * @param  list the list to be "wrapped" in a synchronized list.
     * @return a synchronized view of the specified list.
     */
    public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }

用静态工厂方法代替构造器的第四大优势在于,在创建参数化类型实例的时候,他们是的代码更简洁

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

不能子类化就是不能被继承。那怎么办呢?可以使用复合。
复合,定义一个成员变量,用于引用没有构造器只有静态工厂方法的类,例:

public class Demo {
    SunDemo d = null;
    @Test
    public void DemoTest(){
        d=SunDemo.value();
        d.say();
    }
}

静态工厂方法的第二个缺点在于,他们与其他静态方法实际上没有任何区别

因此,对于提供了静态工厂方法而不是构造器的类来说,要想知道如何实例化这个类,是非常困难的。
解决办法是,注释说明,并遵守标准名称习惯,下面是静态工厂方法额常用名称:

  • valueOf,返回的实例与他的承诺书具有相同的值,这其实是类型转换方法
  • of,valueOf的简介替代
  • getInstance,返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对于单例来说,该方法没有参数,并返回唯一的实例
  • newInstance,同getInstance,但是newInstance能够确保返回的每个实例都是不同的
  • getType,type标示工厂方法所返回的对象类型
  • newType,type标示工厂方法所返回的对象类型

简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解他们的长处。静态工厂方法通常更加合适,因此切忌第一反应就是提供公有构造器,而不是先考虑静态工厂方法。

(全文完)

(转载本站文章请注明作者和出处 考虑用静态工厂方法代替构造器