管理具有许多字段的类

5

我有一个如下的类:

public class Foo {
    private double A;
    private double B;
    ...
    private double K;
}

它应该包含11个紧密绑定的参数A-K,描述了地球轨道上一个点的运动(一种坐标)。我的意思是它们不能被分成子类或其他有意义的部分,因为它们都具有相同的目的和含义。所有这些参数应该在构造函数中一起实例化,以便另一个类可以使用Foo和这11个字段进行必要的计算。 有人对构造函数中参数的数量提出了评论,认为太多了。
是否有另一种初始化Foo对象的方法,而不使用庞大的构造函数,例如使用某种映射?希望我表达得足够清楚,如果不清楚,我将提供更多细节。

你不能使用一个数组来代替从 A-K 的参数吗? - Chirag Parmar
1
建造者模式。 - bradimus
根据您所描述的情况,似乎一个11参数构造函数是最合适的实现。因为出于一般编译器/分析警告而选择不同的实现(如列表)违背了这种警告的根本原因。顺便说一下:空间中的11个点?这是为弦理论吗? - pathfinderelite
这是用于天文历表数据,卫星在地球同步轨道上的位置。使用数组会显得笨拙,因为每个参数都有自己的名称(例如正弦振荡,速率变化,余弦振荡,速率变化等等)。 - borgmater
使用Lombok是一个选择吗?它可以通过一个注解为您生成一个正确且可用的Builder模式。https://projectlombok.org/features/Builder - undefined
4个回答

3
你可以在构造函数中使用 varargsdouble 类型参数,并检查其大小以确保它符合预期。 例如:
public class Foo {
    private double A;
    private double B;
    ...
    private double K;

    public Foo(double... coordinates) {
        if (coordinates == null || coordinates.length != 11) {
            throw new IllegalArgumentException("Unexpected size of coordinates");
        }
        this.A = coordinates[0];
        this.B = coordinates[1];
        ...
        this.K = coordinates[10];
    }
    ...
}

这样,你在构造函数中只需要定义一个参数,但是为了简单起见,你仍然可以提供11个值,如下所示:
Foo foo = new Foo(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0);

您仍然可以将其提供为下一个 double 数组:

Foo foo = new Foo(new double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0});

非常有用,谢谢!不幸的是,如果用户上传参数顺序混乱的配置文件,将没有任何办法知道哪个值对应于特定的参数。 - borgmater
我认为你应该遵循通用约定,例如在3D中,如果我提供坐标{1,2,3},那么我隐含地知道x = 1y = 2z = 3,当然也可以反过来,但那样就完全不符合标准了。 - Nicolas Filotto
1
很抱歉,您无论如何都没有数百万种可能性。您可以选择保留现有内容并忽略警告,或者选择像这个基于数组或集合的解决方案。 - Nicolas Filotto
1
是的,似乎是这样。我已经将它作为解决方案包含在内了,现在我只有一个参数,谢谢 :) - borgmater

1
如果构造函数的调用者需要以一致的方式设置11个紧密绑定的参数,我会使用这样的构造函数,或者像已经说过的那样使用Array或List。
如果您有问题或假设这11个参数的一致性存在问题,那么我更喜欢使用工厂创建Foo。如果此工厂使用具有11个参数或11个set方法调用的构造函数,则取决于您对此类设计的愿望。
您可以使用不同的构造函数和其他参数来代替Factory类,并在这些不同的构造函数中设置11个参数的逻辑。

1
主要危险在于对象的用户可能会混淆参数,将A的值传递给B。
答案取决于具体情况。
如果这些对象是一堆从某些数据源(如配置文件或数据库表)初始化的单例,则需要向构造函数传递一个接口。
interface FooData {
    double getA();
    ...
}

然后在表格或配置文件上实现该接口。

如果对象是根据即时状态动态创建的,则需要使用工厂模式和构建器模式的某种组合。如果存在共同的值集(例如,A只能为1.0或0.0),则使用工厂模式进行提取。使用构建器模式使错误更难发生。

在第二种情况下,在工厂和构建器之后,对象仍将具有11个参数的构造函数,只是对外部世界隐藏了它。


1

我认为这里最大的问题是您有11个相同类型的参数,并且在混合它们时编译器没有任何帮助。为了解决这个问题,您可以考虑以下方法:

public class AValue {
  public final double val;
  public AValue(double val) { this.val = val; }
}

然后,虽然有点丑,但(也许?)有用:将其复制10次,最终得到AValue到KValue的11个类。

然后您可以编写以下构造函数:

public Foo(AValue a, BValue b, ... and so on) {

这允许一个干净、编译器支持的接口来构建Foo。

这并不意味着Foo必须存储11个对象;它可以将它们推入某个有11个插槽的双重数组中。当然,您还可以添加类型安全的方法,例如

AValue getA()

针对你的Foo类。

除此之外,你甚至可以参考Nicolas的答案和这个:

interface ValueType { public double getValue(); }
class AValue implements ValueType {
  ...
  @Override
  double getValue() { return value; }

使用

public class Foo {

  public Foo(AValue a, BValue b, ... KValue k) {
     this((ValueType) a, ..., (ValueType) k);
  }
  Foo(ValueType... values) {
    ... push values into double[]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接