本文参考

本篇文章参考自《Effective Java》第三版第一条"Consider static factory methods instead of constructors"

另外参考了其它几篇文章的解读:

https://www.cnblogs.com/dyj-blog/p/8867028.html

https://blog.csdn.net/u014129886/article/details/89670049

前言

第一条的篇章来自"Creating and Destroying Objects",讲述的是何时并且如何创建对象,销毁对象,所以第一条的"静态工厂方法"不同于软件设计模式中的"工厂方法"(Factory Method Pattern),二者没有任何直接的关联

First Advantage —— they have names

构造方法的方法名始终与类名相同,当我们有不同的构造方法时,就需要保证不同的构造方法有不同的形参类型、形参个数以及形参顺序,以此实现构造方法的重载

原文将这种构造方式称为"a really bad idea",因为当构造方法增多时,显然我们难以判断每个构造方法将会构造怎样的实例,我们需要为每个构造方法书写清晰准确的代码注释,防止选择了错误的构造方法构造实例

但是静态工厂方法不同于构造方法,它的方法名不受类名的约束,我们可以根据返回的实例的具体用途或具体特征来设计方法名,原文举了一个BigInteger的一个例子如下:

For example, the constructor BigInteger(int, int, Random), which returns a BigInteger that is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime.

也因为静态工厂方法可以拥有自己独特的名字,所以我们可以在不同的静态工厂方法中调用相同方法签名的构造方法,不过为此构造方法传入了不同的实参

Second Advantage —— they are not required to create a new object each time they're invoked

从上面一个Advantage来看,我们需要在静态工厂方法内调用构造方法,实际上我们可以直接返回一个静态成员字段,这个字段就是这个类的实例

静态成员字段随类的装载便进行构建,也可以在第一次被用到时才进行实例化,这分别对应了单例模式下的饿汉模式和懒汉模式

当然我们也可以使用Map或List等数据结构来缓存实例化后的对象,当再次调用静态工厂方法时,可以直接从集合中进行获取

构造方法显然无法完成这一点,我们每次使用new关键字都会产生一个新的实例化对象

原文称这种静态工厂方法的解决方案为"instance-controlled"

Instance control allows a class to guarantee that it is a singleton or noninstantiable. Also, it allows an immutable value class to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern.(享元模式)

Third Advantage —— they can return an object of any subtype of their return type

构造方法只能返回本类的实例化对象,但是静态工厂方法能够返回本类的子类的实例化对象,提供更好的灵活性

我们还可以将子类的权限修饰符设置为private,从而精简API并隐藏更多的类实现信息

原文称这种静态工厂方法的解决方案为"interface-based framework"

One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks, where interfaces provide natural return types for static factory methods.

Fourth Advantage —— the class of the returned object can vary from call to call as a function of the input parameters

根据静态工厂方法的参数值不同,返回的对象的类也可以不同,这一条Advantage和上一条Advantage有一点相似之处, 如静态工厂方法所处的类有多个子类,那么在静态工厂方法内可以根据不同的条件返回不同的子类实例

我们用EnumSet抽象类作例,EnumSet抽象类有两个子类RegularEnumSet和JumboEnumSet,后者支持存储超过64个元素,前者只能存储64个以内,在EnumSet的noneOf()方法中,会根据Enum长度的不同,返回不同的子类

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);
}

Fifth Advantage —— the class of the returned object need not exist when the class containing the method is written

方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在

原文称这种静态工厂方法的解决方案为"service provider frameworks",它有如下四个组件:

  • service interface -> represents an implementation of service
  • service provider interface (optional) -> describes a factory object that produce instances of the service interface
  • service access API -> clients use it to obtain instances of the service
  • provider registration API -> providers use it to register implementations (service interface or service provider interface)

若用Jdbc作例,上述四个组件对应如下:

  • service interface -> interface Connection
  • service provider interface -> interface Driver
  • service access API -> DriverManager.getConnection()
  • provider registration API -> DriverManager.registerDriver()

他们的作用分别为:

  • Connection接口具备连接数据库的服务
  • Driver接口的connect()方法返回Connection服务接口实例,是对Connection接口的又一层封装
  • 用户使用DriverManager.getConnection()方法获取连接数据库的服务
  • 服务提供者使用DriverManager.registerDriver()注册Driver实例

注意,从Java 6开始,Java提供了ServiceLoader服务提供者框架,不需要我们自行实现上面四个组件

Since Java 6, the platform includes a general-purpose service provider framework, java.util.ServiceLoader, so you needn't, and generally shouldn't, write your own (Item 59). JDBC doesn't use ServiceLoader, as the former predates the latter.

First Disadvantage —— classes without public or protected constructors cannot be subclassed

因为实例通过静态工厂方法返回,构造方法的权限修饰符通常设置为private,所以该类不可被继承

我们可以通过复合来解决这个缺陷

Arguably this can be a blessing in disguise because it encourages programmers to use composition instead of inheritance, and is required for immutable types.

Second Disadvantage —— they are hard for programmers to find

在Java doc中,构造方法的描述在比较靠前的位置,因此比较容易查阅,但是静态工厂方法的描述不仅比较靠后,而且和普通的静态方法混杂在一起,导致我们难以区分

我们可以通过统一命名规范来解决这个问题

  • from() -> 类型转换方法,它只有单个参数,返回该类型的一个相对应的实例
  • of() -> 聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来
  • valueOf() -> 比 from 和 of 更烦琐的一种替代方法
  • instance() 或者 getInstance() -> 返回的实例是通过方法的(如有)参数来描述,但是不能说与参数具有同样的值
  • create()或者 newInstance() -> 像instance 或者 getInstance 一样,但 create 或者 newInstance 能够确保每次调用都返回一个新的实例
  • getType() -> 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型
  • newType() -> 像newInstance 一样,但是在工厂方法处于不同的类中的时候使用。Type 表示工厂方法所返回的对象类型
  • type() -> getType 和 newType 的简化版

Effective Java —— 用静态工厂方法代替构造器的更多相关文章

  1. Java - 用静态工厂方法代替构造器

    Effective Item - 考虑用静态工厂方法代替构造器我们有两种常见的方法获得一个类的实例: 公有的构造器 提供静态工厂方法(static factory method) 相对公有的构造器,静 ...

  2. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

  3. effective java 3th item1:考虑静态工厂方法代替构造器

    传统的方式获取一个类的实例,是通过提供一个 public 构造器.这里有技巧,每一个程序员应该记住.一个类可以对外提供一个 public 的 静态工厂方法 ,该方法只是一个朴素的静态方法,不需要有太多 ...

  4. 【Effective Java读书笔记】创建和销毁对象(一):考虑使用静态工厂方法代替构造器

    类可以提供一个静态方法,返回类的一个静态实例,如Boolean包装类的一个获取实例的静态方法 public static Boolean valueOf(boolean b) { return (b ...

  5. Effective java读书札记第一条之 考虑用静态工厂方法取代构造器

    对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...

  6. 【读书笔记 - Effective Java】01. 考虑用静态工厂方法代替构造器

    获取类的实例有两种方法: 1. 提供一个公有的构造器(最常用). 2. 提供一个公有的静态工厂方法(static factory method). // 静态工厂方法示例 public static ...

  7. 改善JAVA代码01:考虑静态工厂方法代替构造器

    前言 系列文章:[传送门]   每次开始新的一本书,我都会很开心.新书新心情. 正文 静态工厂方法代替构造器 说起这个,好多可以念叨的.做了一年多的项目,慢慢也有感触. 说起构造器 大家很明白,构造器 ...

  8. 高效JAVA之用静态工厂方法代替构造器

    程序员这行干的久了,总会染上一些恶习,我就染上一个让人深恶痛绝,自己却津津乐道的习惯,还不想改的那种,它可以叫做强迫症,也可以叫做洁癖.那就是我不允许我的IDEA出现一点点警告,什么黄色背景,绿色波浪 ...

  9. Effective Java 之-----静态工厂与构造器

    一. 考虑用静态工厂方法代替构造器: 1)静态工厂方法与构造器不同的第一大优势在于:他们有名称.当一个类需要多个带有相同签名的构造器时,就用静态方法代替构造器,并慎重的选择名称以突出他们间的区别: 2 ...

随机推荐

  1. 用python写九九乘法表

    用python来写九九乘法表,九九乘法表的结构是这样子的: 第一行是1 * 1 = 1,第二行是1 * 2 = 2 | 2 * 2 = 4...以此类推.注意到没,每一行的第一个乘的数字在从1到当行变 ...

  2. SQL Server 2005 - 让 SELECT 查詢結果额外增加递增序号

    /* 方法一*/SELECT 序號= (SELECT COUNT(客戶編號) FROM 客戶 AS LiMing                 WHERE LiMing.客戶編號<= Chan ...

  3. AHUACM寒假集训VI(网络流)

    luoguP2472.蜥蜴 传送门 题目大意: R × C ( 1 ≤ R , C ≤ 20 ) R\times C(1\leq R,C\leq20) R×C(1≤R,C≤20)的网格上,每个格子有一 ...

  4. 使用Python绘制彩色螺旋矩阵

    from turtle import* #导入turtle库 bgcolor("black") #设置画布颜色为黑色 speed(0) #设置画笔绘制速度 colors=[&quo ...

  5. Django的models由数据库表生成

    Django的models由数据库表生成 参考文献:https://www.cnblogs.com/sukura/p/14306510.html 目的或效果:方便实现使用django原生的后台管理系统 ...

  6. 微服务从代码到k8s部署应有尽有大结局(k8s部署)

    我们用一个系列来讲解从需求到上线.从代码到k8s部署.从日志到监控等各个方面的微服务完整实践. 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中 ...

  7. tensorflow源码解析之common_runtime拾遗

    把common_runtime中剩余的内容,按照文件名排序进行了简单的解析,时间原因写的很仓促,算是占个坑,后续有了新的理解再来补充. allocator_retry 有时候内存分配不可能一次完成,为 ...

  8. 手把手建立Roofline模型(CPU)

    Roofline模型原理 Roofline模型是由加州理工大学伯利克提出的用来建立当前计算平台在不同的计算强度(Operational Intensity)下能够达到的理论计算上限 .论文和基础理论和 ...

  9. 说说如何安装 Openfire

    Openfire 是一个基于 XMPP 协议的 IM 服务框架.这里我们来说一说如何安装它. 1 下载 zip 安装包 首先下载 Openfire 安装包,下载路径为:http://www.ignit ...

  10. 为什么Arrays.asList() 不能使用其修改方法(阿里开发手册里明确说明)

    Arrays.asList()方法把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException(),我们来 ...