Java架构师教你写代码(二) - 使用建造者替代多参数的构造器(上)

简介: Java架构师教你写代码(二) - 使用建造者替代多参数的构造器(上)

静态工厂和构造器的局限:对于大量可选参数情况,难以做到很好的扩展。


比如一个类,表示包装食品上的营养标签。

有些字段是必需的:净含量、毛重和每单位份量的卡路里,

还有 20 个可选字段,如:总脂肪、饱和脂肪、反式脂肪、胆固醇、钠…

大多食品只使用可选字段中的少数,且非零值。


这样的类怎么编写构造器或静态工厂?

SE 通常使用可伸缩构造器模式:只向构造函数提供必需的参数。

提供的第一个构造器只有必需参数,第二个构造器有一个可选参数…以此类推,最后一个构造函数具有所有可选参数。

1 伸缩式构造器模式

// 伸缩式构造器模式 - 伸缩性差
public class NutritionFacts {
    private final int servingSize; // (mL) 必须字段
    private final int servings; // (per container) 必须字段
    private final int calories; // (per serving) 可选
    private final int fat; // (g/serving) 可选
    private final int sodium; // (mg/serving) 可选
    private final int carbohydrate; // (g/serving) 可选
    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }
    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }
    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }
    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }
    public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }
}

想创建一个实例,就使用参数列表最短构造器:

NutritionFacts cocaCola =new NutritionFacts(240, 8, 100, 0, 35, 27);

该构造器包含许多额外参数,而且还必须得给它们传递值。

本例中,为 fat 传递了一个0。只有六个参数时,这可能看起来不拉几,但随着参数增加,很快失控。


可伸缩构造器模式可以用,但当有很多参数时,客户端代码很难写,可读性也差 。

阅读者想知道这些值啥意思,必须清点参数。而长序列的相同类型参数也极易导致bug。

如果调用不小心颠倒俩参数,编译器不报错,但程序在运行时会出错。


对于许多可选构造器参数,另一可行方案是

2 JavaBean 模式

调用无参构造器创建对象,然后调用 setter 方法设置所需参数和感兴趣的可选参数。

2.1 实例

4.png

It is easy, if a bit wordy(adj.冗长的), to create instances, and easy to read the resulting(v.产生;adj.作为结果的) code:


2.2 优点

该模式没有可伸缩构造函数模式的缺点。创建实例很容易,虽有点冗长,但可读性较好。

image.png

2.3 缺点

  • 因为构造过程被拆成多个set调用,所以 JavaBean 在并发下构造过程可能处于不一致。无法仅通过校验构造器参数的有效性来保证一致性。在不一致的状态下尝试使用对象可能会导致错误的发生,这比包含bug的代码还难调试。

JavaBean 模式还泯灭了使类不可变的可能性,且需SE费心思确保线程安全。

通过在对象构造完成时手动「冻结」对象,并在冻结之前不允许使用对象,可以减少这些缺陷,但是这种变通方式很笨拙,在实践中很少使用。此外,它可能在运行时导致错误,因为编译器不能确保程序员在使用对象之前调用它的 freeze 方法。


幸好,还有第三种方案,它结合可伸缩构造器模式的安全性和 JavaBean 模式的可读性

3 建造者模式

  1. 不直接生成所需对象,而使用所有必需参数调用构造器(或静态工厂),获得一个 builder 对象
  2. 然后客户端在构建器对象上调用 setter 方法设置每个感兴趣的可选参数
  3. 最后调用一个无参build方法来生成对象,这通常是不可变的。builder通常是它构建的类的静态成员类。

3.1 实例

5.png

NutritionFacts 类不可变,所有默认参数值都在一个位置。builder的 setter 方法返回builder本身,便于链式调用,得到流式 API。形如下:

image.png

特点

这样的代码易于编写,可读性佳。

为简洁,省略有效性检查。为尽快检测到无效参数,可在builder的构造器和方法中校验参数有效性。检查不可变量,包括build方法调用的构造器中的多个参数。为确保这些不可变量免受攻击,从builder复制参数后检查对象字段。如果检查失败,抛 IllegalArgumentException,指示哪些参数无效。


目录
相关文章
|
1月前
|
Java 开发工具
【Azure Storage Account】Java Code访问Storage Account File Share的上传和下载代码示例
本文介绍如何使用Java通过azure-storage-file-share SDK实现Azure文件共享的上传下载。包含依赖引入、客户端创建及完整示例代码,助你快速集成Azure File Share功能。
337 4
|
1月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
238 7
|
1月前
|
Java 数据处理 API
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
为什么你的Java代码应该多用Stream?从循环到声明式的思维转变
234 115
|
1月前
|
安全 Java 编译器
为什么你的Java代码需要泛型?类型安全的艺术
为什么你的Java代码需要泛型?类型安全的艺术
172 98
|
1月前
|
Java 编译器 API
java最新版和java8的区别,用代码展示
java最新版和java8的区别,用代码展示
243 43
|
1月前
|
前端开发 JavaScript BI
如何开发车辆管理系统中的车务管理板块(附架构图+流程图+代码参考)
本文介绍了中小企业如何通过车务管理模块提升车辆管理效率。许多企业在管理车辆时仍依赖人工流程,导致违章处理延误、年检过期、维修费用虚高等问题频发。将这些流程数字化,可显著降低合规风险、提升维修追溯性、优化调度与资产利用率。文章详细介绍了车务管理模块的功能清单、数据模型、系统架构、API与前端设计、开发技巧与落地建议,以及实现效果与验收标准。同时提供了数据库建表SQL、后端Node.js/TypeScript代码示例与前端React表单设计参考,帮助企业快速搭建并上线系统,实现合规与成本控制的双重优化。
|
1月前
|
安全 Java 容器
告别空指针噩梦:Optional让Java代码更优雅
告别空指针噩梦:Optional让Java代码更优雅
358 94
|
安全 Java 开发者
Effective Java - 构造器私有、枚举和单例
Singleton 是指仅仅被实例化一次的类。Singleton代表了无状态的对象像是方法或者本质上是唯一的系统组件。使类称为Singleton 会使它的客户端测试变得十分困难。因为不可能给Singleton替换模拟实现。除非实现一个充当其类型的接口
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
143 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
162 1

热门文章

最新文章