2008-11-20 33 views
5

我非常好奇为java bean提供不可变性的可能性(这里的bean指的是具有为构造成员提供getter和setter的空构造函数的类)。很明显,这些类不是不可变的,它们被用来从数据层传输值,这似乎是一个真正的问题。Java中的不可变bean

这个问题的一种方法在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显然有大量的样板。

我非常有兴趣听到别人解决这个问题的方法。 (我不提供一个很好的问题,这都可以回答,而不是讨论:)

回答

2

我想我会使用委托模式 - 使一个ImmutableDate类必须在构造函数中指定了一个DateBean成员:

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。那么为任何对象构建代理,委派所有获取方法以及根据需要捕获或忽略集合方法将是相对容易的。

3

有些意见(不一定是问题)道歉:

  1. Date类本身是可变的,所以你正确地将其复制到保护不变性,但我个人更倾向于在构造函数中将long转换为long,并在getter中返回一个新的Date(longValue)。
  2. 您的getWhateverInstance()方法都会返回需要投射的DateBean,因此可以将接口更改为返回特定类型。
  3. 说了这么多,我会倾向于只有两个类,一个是可变的,另一个是不可变的,如果合适的话,共享一个共同的(即只得到)接口。如果你认为会有很多来回转换,那么为这两个类添加一个拷贝构造函数。
  4. 我更喜欢使用不可变类来声明字段为final以使编译器强制执行不变性。

例如

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(); 
    } 

} 
+0

感谢您的意见。我同意复制构造函数是对接口工厂方法的重大改进。关于最终成员的说明很好。干杯。 – 2008-11-21 00:44:52

1

我使用接口和铸造来控制bean的可变性。我没有看到用getImmutableInstance()getMutableInstance()等方法使域对象复杂化的好理由。

为什么不只是利用继承和抽象?例如

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

+0

我假设你是指MutableUser接口来扩展用户界面吗?不用说,这仍然需要一个具体的课。不利的一面是,它打开你“不可变”被改变可变通过简单的演员,这可能不是你想要的。 – 2008-11-21 00:48:45

+0

虽然这是事实的是,用户界面不提供变异用户对象不是在脸上它作为不变性,我会要求从这种模式的强有力的保证方法。 – 2008-11-21 00:51:01