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...

总结

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

相关推荐
Tigshop开源商城18 小时前
『物流设置+SEO优化』Tigshop开源商城系统 JAVA v5.8.26 版本更新!
java·开源商城系统·tigshop
Tigshop开源商城20 小时前
『订单税率+收货地址校验国家字段』功能上新|跨境运营更高效,Tigshop开源商城系统 JAVA v5.8.23 版本更新
java·开源商城系统·tigshop
养肥胖虎20 小时前
Docker学习笔记:后端、数据库和反向代理怎么一起跑起来
后端·nginx·docker·postgresql·go·部署
REDcker20 小时前
C++变量存储与ELF段布局详解 从const全局到rodata与nm_readelf验证实践
java·c++·面试
晓杰'20 小时前
从0到1实现 Balatro 游戏后端(2):NestJS框架搭建与项目结构设计
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs
无所事事O_o21 小时前
二次验证码TOTP 使用说明
后端·二次验证码·谷歌验证器
ltl21 小时前
Multi-Head Attention:为什么要分多个头
后端
kobesdu21 小时前
【ROS2实战笔记-19】ROS2 生命周期节点的启动顺序、状态转换陷阱与热备方案
java·前端·笔记·机器人·ros·ros2
neo_Ggx2321 小时前
Maven 版本管理详解:SNAPSHOT、Release 与 Nexus 仓库的区别和影响
java·maven
matlabgoodboy21 小时前
软件开发定制小程序APP帮代做java代码代编写C语言设计python编程
java·c语言·小程序