2008-11-20 72 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