Java中的不可变bean
-
08-07-2019 - |
题
我非常好奇为java bean提供不变性的可能性(这里的bean我指的是带有空构造函数的类,为成员提供getter和setter)。显然,这些类不是不可变的,并且它们用于从数据层传输值,这似乎是一个真正的问题。
StackOverflow中提到了一个解决这个问题的方法,叫做<!>“C#<!>”中的不可变对象模式。完全构建后对象被冻结的位置。我有另一种方法,并且非常希望听到人们的意见。
模式涉及两个类Immutable和Mutable,其中Mutable和Immutable都实现了一个提供非变异bean方法的接口。
例如
public interface DateBean {
public Date getDate();
public DateBean getImmutableInstance();
public DateBean getMutableInstance();
}
public class ImmutableDate implements DateBean {
private Date date;
ImmutableDate(Date date) {
this.date = new Date(date.getTime());
}
public Date getDate() {
return new Date(date.getTime());
}
public DateBean getImmutableInstance() {
return this;
}
public DateBean getMutableInstance() {
MutableDate dateBean = new MutableDate();
dateBean.setDate(getDate());
return dateBean;
}
}
public class MutableDate implements DateBean {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public DateBean getImmutableInstance() {
return new ImmutableDate(this.date);
}
public DateBean getMutableInstance() {
MutableDate dateBean = new MutableDate();
dateBean.setDate(getDate());
return dateBean;
}
}
这种方法允许使用反射(按照惯例)构造bean,并允许我们在最近的机会转换为不可变的变量。不幸的是,每个bean显然有大量的样板。
我很想听听别人对这个问题的处理方法。 (我抱歉没有提出一个好的问题,可以回答而不是讨论:)
解决方案
我想我会使用委托模式 - 使用必须在构造函数中指定的单个DateBean成员创建一个ImmutableDate类:
public class ImmutableDate implements DateBean
{
private DateBean delegate;
public ImmutableDate(DateBean d)
{
this.delegate = d;
}
public Date getDate()
{
return delegate.getDate();
}
}
如果我需要在DateBean d上强制使用不变性,我只需要新的ImmutableDate(d)。我可以很聪明,并确保我没有委派代表,但你明白了。这避免了客户试图将其转变为可变的问题。这很像JDK使用Collections.unmodifiableMap()等(在这些情况下,仍然必须实现变异函数,并编码为抛出运行时异常。如果你有一个没有的基本接口,那就容易多了。存取器)。
然而,它再次是冗长的样板代码,但只需点击几下鼠标就可以为您自动生成像Eclipse这样的好IDE。
如果你最终对很多域对象做了什么,你可能想要考虑使用动态代理甚至是AOP。那么为任何对象构建代理,委派所有get方法,以及根据需要捕获或忽略set方法会相对容易。
其他提示
一些评论(不一定是问题):
- Date类本身是可变的,所以你正确地复制它以保护不变性,但我个人更喜欢在构造函数中转换为long并在getter中返回一个新的Date(longValue)。
- 你的getWhateverInstance()方法都返回了必须进行强制转换的DateBean,可能是改变界面以返回特定类型的想法。
- 说了所有我倾向于只有两个类,一个是可变的,一个是不可变的,如果合适的话,共享一个公共(即get get)接口。如果您认为会有很多来回转换,那么请在两个类中添加一个复制构造函数。
- 我更喜欢不可变类将字段声明为 final ,以使编译器也强制执行不变性。 醇>
e.g。
public interface DateBean {
public Date getDate();
}
public class ImmutableDate implements DateBean {
private final long date;
ImmutableDate(long date) {
this.date = date;
}
ImmutableDate(Date date) {
this(date.getTime());
}
ImmutableDate(DateBean bean) {
this(bean.getDate());
}
public Date getDate() {
return new Date(date);
}
}
public class MutableDate implements DateBean {
private long date;
MutableDate() {}
MutableDate(long date) {
this.date = date;
}
MutableDate(Date date) {
this(date.getTime());
}
MutableDate(DateBean bean) {
this(bean.getDate());
}
public Date getDate() {
return new Date(date);
}
public void setDate(Date date) {
this.date = date.getTime();
}
}
我使用接口和转换来控制bean的可变性。我没有看到使用getImmutableInstance()
和getMutableInstance()
等方法使域对象复杂化的充分理由。
为什么不只使用继承和抽象? e.g。
public interface User{
long getId();
String getName();
int getAge();
}
public interface MutableUser extends User{
void setName(String name);
void setAge(int age);
}
以下是代码客户端将要执行的操作:
public void validateUser(User user){
if(user.getName() == null) ...
}
public void updateUserAge(MutableUser user, int age){
user.setAge(age);
}
它能回答你的问题吗?
YC