我需要重载构造函数吗?构造函数的每个组合的工厂方法 |工厂方法的参数?

问题描述 投票:0回答:1

我有4个可以默认初始化的实例变量,下面写的代码合适吗?构造函数的数量正在以惊人的速度增长,我会使用具有 getter 和 setter 的 Builder 对象吗?

 class Example {
    private int a;
    private String s;
    private Object object;
    private boolean aBoolean;


    public Example(int a1, String s1, Object object1, boolean aBoolean1) {
        a = a1;
        s = s1;
        object = object1;
        aBoolean = aBoolean1;
    }

    public Example(int a1, String s1, Object object1) {
        this(a1, s1, object1, true);
    }

    public Example(int a1, String s1) {
        this(a1, s1, new Object(), true);
    }

    public Example(int a1,boolean aBoolean1) {
        this(a1, "a String", new Object(), aBoolean1);
    }
// ...(for all other combinations)
}
java constructor overloading factory-method constructor-overloading
1个回答
3
投票

本质上来说,不。作为一个概念,这无法扩展。有两种方式。

为什么你的风格不起作用

组合爆炸

假设您有 6 个字段,并且它们都有默认值。这意味着您正在创建 2 到 6 个不同的构造函数(64 个构造函数)。除了表面上看很疯狂而且肯定无法维护这一事实之外,即使您决定使用一些自动化工具或注释处理器来生成它们,由于类文件的限制,您可以拥有的数量也有 65536 个限制。

类型冲突

假设您有 2 个字段(

a
b
),并且它们都有默认值。这两个字段的类型都是
int
。你不能这样做 - 仅采用
a
并保留
b
为默认值的构造函数与仅采用
b
并保留
a
为默认值的构造函数冲突 - 两者都具有签名
YourType(int)

常见解决方案

仅限从左到右

不要创造组合爆炸,只需继续前进。因此,假设一个类有 3 个字段 (

int a; int b; String c;
),你就有 4 个构造函数:

  • ()
  • (int a)
  • (int a, int b)
  • (int a, int b, String c)

换句话说,如果还提供了“下方”的任何字段,则给定“字段”是必需的。然后以合理的顺序对字段进行排序。这使您拥有的构造函数的数量保持在可管理的范围内(如果它们是默认的 1+n,这与您拥有的字段数量成线性关系,而不是 fac(n) 爆炸)。它或多或少也符合 Java 程序员的期望,并且它与其他各种类似工作的语言相匹配。 只有全和无

唯一存在的构造函数是无参数构造函数,因为各种工具都需要它,还有全参数构造函数。

懂事

考虑 API,考虑使用您的类的代码将如何运行。是否有一组有限的常见且明显的方法来构建它?如果是,请编写这些构造函数。根据定义,没有办法告诉你应该在给定字段类型列表的情况下编写哪些类型 - 你需要准确解释你的类型代表什么以及它的作用,它是“情人眼中的”。 API 设计带有一丝艺术气息。您决定如何使用它。

建设者

这是真正适用于各种情况的“一劳永逸”解决方案。你写一个构建器。构建器的本质意味着您的 API 用户(调用此类/此类实例上的内容的代码)最终会编写类似这样的内容。

// given: class Bridge { int yearBuilt; int span; String name; } // do not construct like this: new Bridge(1937, 1280, "Golden Gate");

你不这样构建的原因不仅仅是
,因为它使得让每个参数都是可选的变得非常棘手。另外,调用本身也变得不可读。那是一座建于1937年、最大跨度为1280米的桥,还是一座建于1280年、最大跨度为1937米的桥?如果没有 javadoc 或让我的 IDE 突出显示这些参数的名称,则无法从这个调用中看出。

相反,你让你的图书馆用户写下这个:

Bridge.builder() .buildYear(1937) .maxSpan(1280) .name("Golden Gate") .build();

这有各种各样的优点:

每个参数都可以是默认/可选的,不会出现

fac(n)
    爆炸 - 它与字段数量成线性关系。
  • 您可以创建多种替代方法来指定给定字段值。也许
    .buildDate(LocalDate.of(1937, 4, 19))
  • 还有
  • .buildDate(1937, Month.APRIL, 19)
    ——如果你真的想要那样,至少它不会让组合爆炸效果变得更糟。
    调用者拥有可读的代码 - 现在只要查看该代码,我就不可能混淆构建年份的跨度。
  • 后来添加默认值不会破坏现有代码。添加非默认的东西 - 这比较棘手。
  • 它相对常见,所以对于普通的 java 程序员来说应该很熟悉。
  • 缺点是,编写构建器以及使其工作所需的所有样板方法需要大量的样板文件(不仅仅是
  • 每个字段的构建类中的“setter”,而且
builder()

build()、私有构造函数等等)。 Project Lombok 会为您生成所有这些:

@Builder
 文档
。即使您不能使用 lombok/不喜欢它,文档也包含

@Builder

生成的“普通”版本的示例,它可以作为如何自行操作的示例。


© www.soinside.com 2019 - 2024. All rights reserved.