Effective Java 第三版阅读笔记——条款1.考虑使用静态工厂方法替代构造器

获取一个类的实例的传统方法是使用公开的构造器,除此之外,一个类还可以提供公开的静态工厂方法(static factory method)来返回它的实例。例如 Boolean 类中的 valueOf 方法,这个方法将基本类型 boolean 转换为一个 Boolean 对象的引用:

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

:静态工厂方法与设计模式中的工厂方法不同。

静态工厂方法的优点:

  • 与构造器不同,静态工厂方法拥有自己的名字。考虑一个场景: BigInterger 类的一个构造器 BigInterger(int, int, Random) 可能返回一个素数,然而传统的构造器不能很直观地表达出来,我们可以用一个静态工厂方法 BigInteger.probablePrime 使表达变得更清晰。

    此外,如果一个类需要几个参数类型相同的构造器,它只能打乱参数的顺序来进行区别,这使得类的构造变得十分困难。此时可以考虑使用静态工厂方法,这些方法拥有相同的参数类型,但你可以赋予它们精心挑选的名字,使它们的区别变得明显。

  • 当静态工厂方法被调用时,它们不需要每一次都创建一个新的对象。如开篇的 Boolean.valueOf(boolean) 所示,这个方法从来不创建对象,它只是返回 Boolean 类中已经创建的对象:

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

    这有效地提升了程序的性能。这一技巧与设计模式中的享元模式(Flyweight)十分相似。

  • 静态工厂方法可以返回其返回类型的任何子类的对象。一个可能的应用场景:API 可以返回对象,而不需要将它们的类公之于众,以这种方式隐藏实现类将使这个 API 变得十分紧凑。这个技术适合于基于接口的框架(interface-based frameworks)。

  • 随着输入参数的不同,同一个静态工厂方法可以返回不同的子类。例如 EnumSet 类:

    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");
    
        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
    

    当底层枚举类型的元素不超过64个时,返回 RegularEnumSet,超过64个则返回 JumboEnumSet

  • 在编写包含静态方法的类时,返回对象的类不需要存在。这种灵活的静态工厂方法构成了服务提供者框架(service provider frameworks)的基础,比如Java的数据库连接 API(JDBC)。

静态工厂方法的缺点

  • 只提供静态工厂方法、不提供 publicprotected 构造器的类不能被继承(子类化)。这也可能因祸得福:它鼓励程序员使用组合而不是继承。
  • 静态工厂方法很难被程序员找到。在API文档中,静态工厂方法不能像构造器那样突出,这使得程序员难以找到它们来实例化一个类。

一些静态工厂方法的常用名称

  • from:一个类型转换方法,接受一个参数并返回这个类型的相应实例。

    Date d = Date.from(instant);
    
  • of:一个聚合方法,接受多个参数、将它们合并在一起并返回这个类型的相应实例。

    Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
    
  • valueOffromto 的更为详细的替代方式。

    BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
    
  • instancegetInstance:返回一个实例,该实例由其参数(如果有的话)描述,但不能说具有相同的值。

    StackWalker luke = StackWalker.getInstance(options);
    
  • createnewInstance:与 instancegetInstance 类似,除了该方法保证每个调用返回一个新的实例。

    Object newArray = Array.newInstance(classObject, arrayLen);
    
  • getType:与 getInstance 类似,当工厂方法在不同的类中时使用。Type 是工厂方法返回对象的类型。

    FileStore fs = Files.getFileStore(path);
    
  • newType:与 newInstance 类似,当工厂方法在不同的类中时使用。Type 是工厂方法返回对象的类型。

    BufferedReader br = Files.newBufferedReader(path);
    
  • typegetTypenewType 的简洁版。

    List<Complaint> litany = Collections.list(legacyLitany);
    

猜你喜欢

转载自blog.csdn.net/sky_asd/article/details/83757523