更好的创建具有许多属性的对象的方法

4

我有一个领域对象Invoice,它具有大约60个属性,其中一些是必填的,而一些是可选的。这个Invoice类是底层数据库表中记录的表示,使用应用程序层类(例如对于存储在数据库中的简单整数的枚举,对于双精度等货币等)包装某些列值。

目前这个Invoice类的定义如下:

  • 公共完整参数构造函数。
  • 公共getter。
  • 受保护的setter。

现在,对于创建Invoice对象的客户端来说,将所有60个属性传递给构造函数是令人望而生畏的。由于明显的原因,我坚决反对使setter变为public

请问你可以建议更好的方法来允许创建/修改此发票对象吗?如果需要更多详情,请告诉我。


2
很有可能你可以将所有这些属性分解成更小的组,然后将它们包装成类。例如,如果你有一个xposition和一个yposition,你可以使用一个单独的“Point”来包含两者。显然,在你的“Invoice”类中没有位置,但你可能明白我的意思。 - Alexis King
我可以这样做 - 将相关属性分组到一个对象中。这仍然意味着客户端必须构建这些对象来创建发票对象,对吗? - Vikdor
是的,但它会将其解析为更易处理的对象,并且通过这种方式可以拥有更多“默认”值。逐个创建对象然后将它们插入构造函数比传递一行庞大的参数到构造函数更容易。您可以利用子对象构造函数中的运算符重载来提供智能默认值,从而减少客户端的工作量。 - Alexis King
如果您能提供您传递的属性列表中的至少一些,那么我们可能能够更好地帮助您。 - Alexis King
不确定将这些属性添加到问题中是否会违反任何代码(我没有正确阅读条款;) ,但这些是具有供应商属性,货币属性,数量相关属性等的发票的典型属性。 - Vikdor
感谢@JakeKing提出的有关拆分它们的初始想法。我将采用这种方法来使发票对象的创建更加容易。 - Vikdor
4个回答

18

使用建造者模式

使用 Joshua Bloch 在他的书籍 Effective Java 2nd Edition 中描述的建造者模式。您可以在http://www.javaspecialists.eu/archive/Issue163.html中找到相同的示例。

特别注意以下行:

NutritionFacts locoCola = new NutritionFacts.Builder(240, 8) // Mandatory
                          .sodium(30) // Optional
                          .carbohydrate(28) // Optional
                          .build();

使用 BeansUtils.populate

另外一种方法是使用org.apache.commons.beanutils.BeanUtils.populate(Object, Map)这个方法,该方法来自于Apache Commons BeansUtils。在这种情况下,您需要一个Map来存储该对象的属性。

代码:

public static void main(String[] args) throws Exception {

    Map<String, Object> map = new HashMap<>();
    map.put("servingSize", 10);
    map.put("servings", 2);
    map.put("calories", 1000);
    map.put("fat", 1);

    // Create the object
    NutritionFacts bean = new NutritionFacts();

    // Populate with the map properties
    BeanUtils.populate(bean, map);

    System.out.println(ToStringBuilder.reflectionToString(bean,
            ToStringStyle.MULTI_LINE_STYLE));

}

输出:

NutritionFacts@188d2ae[
  servingSize=10
  servings=2
  calories=1000
  fat=1
  sodium=<null>
  carbohydrate=<null>
]

在某些情况下,这是否仍需要传递所有60个属性? - npinti
只有当您需要全部60个属性时才使用@npinti。 - MadProgrammer
1
+1,我知道建造者模式,但@npinti的建议帮助我如何将分解和建造者结合起来,使代码更易于管理和阅读。谢谢。 - Vikdor
@MadProgrammer:是的,我知道,这就是为什么我说“某些情况”。 - npinti

4

您可以将对象分解成更小的对象。如上面的评论所述,您可能需要用户构建这些新对象,但根据您的数据库设计,您可能只需要传递一个主键或外键给该类。

然后,该类将具有一些行为,它将从数据库中查找相关数据。这显然会增加数据库服务器的负载,但它将允许您拥有较少复杂(尽管数量更多)的类。简化复杂性很可能会增加代码可重用性,并使其更易于维护。


+1,将来我会在分解后得到更容易管理的代码块。谢谢。 - Vikdor

0

正如@Jake King所建议的,将60个属性组合成较小的数据对象总是更好的选择。

在这样做的同时,我会考虑一个方面,即哪些组合是可选的,并以那种方式进行组合。例如,如果客户点击使用当前地址和邮寄地址,则邮寄地址是可选的。

围绕这些组合对象构建构造函数将有助于您轻松高效地管理/维护类。


-3
如果您的发票对象有60个属性并且您确定不需要其中一些,则无需为这些属性创建getter和setter。通常建议在代码中创建所需属性的getter和setter,但您需要确保省略的字段允许数据库中的空约束。
另外,如果您需要在代码中使用全部60个属性,则根据客户端的要求创建不同的构造函数。如果客户端只需要传递4个参数,则创建接受4个参数的构造函数,并为未通过客户端传递的属性在数据库中设置默认值。

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