Effective Java 用静态工厂方法代替构造器

序言

最近需求写完, 测试也测完发布到rc环境后, 有一个涉及第三方接口的地方需要做一点调整, 于是就在等待第三方回应的闲暇之余拿起来几年前买的一本好书 << Effective Java>> 第三版来重读下部分内容, 今天分享的就是第二章的第一条: 用静态工厂方法代替构造器.

首先我打开自己写的代码, 尤其是一些业务对象, 大多数的类都是这么定义的:

java 复制代码
@Data
public class TestModel {
    private Long xx;
    private String yy;

使用的时候就是 new TestModel(xx,yy) 这样, 这是非常合乎常理的一种使用方式. 但是这里本书就提到了, 我们程序员的工具箱里应该还有一个创建对象实例的方法, 就是让类提供一个或多个公有的静态工厂方法(static factory method), 而且在Java源码里面就有很多地方用到并且我们会经常用到, 比如: BigDecimal.valueOf, Date.from, LocalDate.of...

静态工厂方法的优势

在有了构造方法后为什么还要这么建议呢? 在书里给出了5条理由:

  1. 有名字
  2. 有需求的时候, 不必每次都创建一个新对象
  3. 返回对象类型可以是返回类型的任何子类型的对象
  4. 返回对象的类型可以根据参数值的变化而变化
  5. 返回对象的具体类型可以不存在

让我来逐个演示下这五个优势的具体体现:

  1. 有名字
java 复制代码
public static TestModel withIdAndName(Long id, String name) {
    TestModel testModel = new TestModel();
    testModel.setId(id);
    testModel.setName(name);
    return testModel;
}

这种随便自定义名称的方法肯定比冰冷的构造器更容易理解.

  1. 有需求的时候, 不必每次都创建一个新对象
java 复制代码
public static TestModel commonInstance() {
    return EmptyModel.INSTANCE;
}

该方法每次放回的都是同一个对象, 我这里实现的方法是通过静态内部类的方式类实现的, 也可以通过其他单例模式的实现来实现该需求. 这里需要注意的是, 由于是同一个对象, 需要考虑引用传递的问题.

  1. 返回对象类型可以是返回类型的任何子类型的对象
csharp 复制代码
public static TestModel modelA() {
    return new ModelA();
}

很明显这里返回的是TestModel的子类 ModelA 的对象实例.

  1. 返回对象的类型可以根据参数值的变化而变化
typescript 复制代码
public <T extends TestModel> TestModel typeModel(T type) {
    if(type instanceof ModelA a) {
        return a;
    }else if(type instanceof ModelB b) {
        return b;
    }
    return null;
}

这个方法返回对象的类型就取决于参数type的具体类型.

  1. 返回对象的具体类型可以不存在
csharp 复制代码
public static PostgresqlConnect connect() {
    return ServiceLoader.load(PostgresqlConnect.class).findFirst().orElse(null);
}

不知道这里我理解的对不对, 这里方法的返回值是一个接口, 具体的实现类在方法调用的时候去指定的位置加载, Java的SPI机制应该也是基于这个基础.

静态工厂方法的劣势

说完这么多优点, 还有两个缺点也要交待一下:

  1. 类如果不含有公有的或者受保护的构造器, 就不能被子类化
  2. 程序员很难去发现他们. 通常在API文档里不会像描述构造器一样去描述静态构造方法, 但是可以通过标准的命名习惯可以些微弥补这个缺点, 列举一些在JDK源码中常用的名称: from, of, valueOf, instance/getInstance, create/newInstance, getType, newType, type...

总结

静态工厂方法和公有构造器各有特点, 在写代码的时候切记第一反应是构造器而完全忽略静态工厂.

相关推荐
桃园码工2 分钟前
第一章:Go 语言概述 2.安装和配置 Go 开发环境 --Go 语言轻松入门
开发语言·后端·golang
robin_suli16 分钟前
Java多线程八股(三)一>多线程环境使用哈希表和ArrayList
java·开发语言·多线程·哈希表
NiNg_1_23420 分钟前
Java中的多线程
java·开发语言
丁总学Java27 分钟前
nohup java -jar supporterSys.jar --spring.profiles.active=prod &
java·spring·jar
呆呆小雅28 分钟前
C# 结构体
android·java·c#
谢尔登29 分钟前
使用 Maven 创建 jar / war 项目
java·maven·jar
理想不理想v41 分钟前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js
赶路人儿43 分钟前
IntelliJ IDEA配置(mac版本)
java·macos·intellij-idea
jjw_zyfx43 分钟前
docker 的各种操作
java·docker·eureka
生财44 分钟前
获取字 short WORD 上指定的位是否有效
java·服务器·c#