我参与了这个项目,我们正在构建良好的遗留代码。我有一个关于一个大的java bean对象的特殊情况,它必须通过线路传输。所以我的第一个想法就是让它变得不可变和可序列化。这时我面临一些困难的选择: -
理想情况下我想要一些方法 自动生成不可变的, 此类的可序列化版本。 我没有重构的范围或 以任何方式改变这个类,我 真的很讨厌 用一个复制粘贴类 不同的名字?
假设我放弃了1,即我 实际上选择重复代码 我还是那个巨大的javabean课程 将处于令人讨厌的境地 必须编写构造函数 用20-25个参数来制作 这个类是不可变的。什么是 使类不可变的更好方法 除了构造函数注入?
谢谢和问候,
答案 0 :(得分:4)
要使其真正不可变,您需要在构建时初始化成员。
一种方法(我并不是说它很漂亮!)要做到这一点,并避免构造函数中的一个巨大的参数列表是一个具有相同属性的可变类型。通过“setters”一次设置一个可变类型的属性,然后将可变对象作为单个参数传递给不可变类型的构造函数。然后,不可变对象将属性从可变源复制到它自己的(final
)成员。
您可能还会考虑“有效的不变性”。也就是说,即使系统没有强制执行不变性,您也可以使用明确将初始化阶段与使用阶段分开的编码实践。毕竟,序列化不需要不变性。
您可以更进一步,为接口创建一个实现隐藏包装器,不会公开实现的属性。包装器只通过委托“真实”实现来实现接口中的方法。包装器中不存在实现中的setter和getter。这将阻止客户端简单地从接口向下转换为实现类并操纵属性。
答案 1 :(得分:4)
Joshua Bloch的“Effective Java”说明了Builder模式,其中简单的Builder对象用于构造具有长构造函数参数列表的复杂对象。我推荐它。
http://www.drdobbs.com/java/208403883;jsessionid=PJWS41F5DJ4QRQE1GHRSKH4ATMY32JVN?pgno=2
答案 2 :(得分:1)
对于一次性而言,20-25属性并不是很大,特别是如果你使用的是一个不错的编辑器。
如果在构造不可变版本时已经有一个可变实例,只需将其传递给构造函数。
如果您想成为真正邪恶的hacky ,请使用java.beans
为实现Map
的可变类或子类创建可序列化Externalizable
。或者,您可以使用java.beans
XML序列化(可以通过Java序列化发送的XML ...)。
答案 3 :(得分:1)
一个简单的只读界面,包括吸气剂?
如果bean类是你自己的,那么让它简单地实现接口并在创建后只使用接口。
如果您无法控制bean类,您还可以创建一个getter接口并通过为getter接口创建一个代理来实现它,并使用invokation处理程序将所有方法调用委托给bean。
答案 4 :(得分:0)
步骤1 :创建一个新类,并为其提供与“大型java bean对象”的实例变量名称完全相同的实例变量。这个新类不应该有setter(只有getter)才能使它成为不可变的。
第2步:使用Apache Commons BeanUtils.copyProperties 将所有属性(即实例变量)从“大型java bean对象”复制到新对象。 / p>
答案 5 :(得分:0)
一些想法:
受保护的制定者和工厂方法 您可以使用受保护的setter方法定义bean,并在同一个包中定义一个工厂类,它接受所有参数并调用这些setter。 bean在该包之外是不可变的。要强制执行此操作,请确保密封您的jar,以便最终用户无法在同一个包中创建新类。
注意:您可以使用我的JavaDude Bean注释来简化创建: http://code.google.com/p/javadude/wiki/Annotations
例如:
@Bean(writer=Access.PROTECTED, // all setXXX methods will be protected
properties={
@Property(name="name"),
@Property(name="age", type=int.class)
})
public class Person extends PersonGen {
}
在eclipse中创建getter和构造函数
Eclipse提供了一些很好的工具来实现这一目标:
Immutability Decorator
另一个想法是使用getter和setter定义你的bean(你可以使用上面的技术但是包含setter),然后你可以为它创建一个只包含getter的包装类。