[英]How to set cardinalities in Java field?
我們假設我們有以下課程:
public class MyClass{
List<String> list;
void method() {
}
}
這個類的每個對象都有一個字符串列表,但如果我們想設置基數怎么辦? 例如,我想強制此類在該列表中至少有 2個字符串。
是否有一般模式來表示字段的基數?
您只需通過某種方式確保list
中至少有2個元素。 沒有標准或簡單的方法來做到這一點。
這被稱為班級的不變量 。 作為編寫課程的人,您有責任確保始終保留此不變量。 特別是:
list
至少包含兩個元素。 當構造函數完成時,實例應該是“有效的”(在其記錄的不變量為真的意義上)。 您需要確保類中的所有代碼在操作列表時遵循不變量 - 您可以暫時使其無效,但您必須確保無法在無效狀態下觀察您的實例。
在單線程的情況下,這只是意味着一旦public方法返回,invariant必須為true(注意這包括是否發生異常); 但是如果您的代碼設計為以多線程方式使用,則還必須確保沒有線程可以在不變量為false的狀態下看到您的實例(例如,您可能需要synchronized
塊,或確保更新是原子的)。
您需要確保您的類的子類不能使不變量為false; 或明確記錄編寫子類的人員有責任確保不變量保持為真。
Effective Java 2nd Ed中有一個很棒的項目:“繼承的設計和文檔,或者禁止它”。
您需要確保類外的任何內容都無法訪問列表的引用。 例如,您的代碼使列表對同一個包中的其他類可見 - 這些類中的任何一個都可以調用theInstance.list.clear()
,並使您的不變量無效。
絕對很難防止這種情況 - 例如,惡意行為者可能有可能使用反射使不變量無效。 你可以防止這種情況,但這是一個權衡識別和阻止這些方法的努力與不變量的實際成本變為錯誤的問題(這在很大程度上取決於如何使用這個類)。
到目前為止,強制執行不變量的最簡單方法是使用不可變類 。 如果無法更改實例的可觀察狀態,則無法使不變量無效。 然后,您需要擔心的是a)確保該類是不可變的; b)確保構造函數返回后不變量為真。 上面所有其他要點就會消失。
是否有一般模式來表示字段的基數?
顯然,您可以使用整數字段來表示基數,無論是在對象本身還是在元級別。 但如果你不能強制執行它們,那就沒什么用了。
沒有一般的模式。 @Andy Turner的回答提供了關於執行基數的替代方案的一個很好的總結。 我只想補充幾點:
嘗試通過靜態類型強制執行基數約束不太可行。 Java類型系統不夠豐富,無法以愉快的方式執行此操作1 。
具有最小基數的字段的對象的構造可能是棘手的,尤其是如果存在涉及這些字段的潛在循環。
處理構造的一種方法是將對象的生命周期分成“構造”階段和“完成”階段。 在施工階段,放松約束,以允許分階段進行施工。 在某些時候,“完成”開關被“翻轉”。 此時1)檢查基數約束,2)改變變異操作的行為以防止違反基數的變化。
這可以使用公共構造函數和公共方法來實現“翻轉開關”。 或者,您可以使用構建器模式實現此操作; 例如
build()
方法中翻轉開關(需要一個build()
。 另一種方法是允許字段低於基數,但只允許在項目處於該狀態時將項目添加到字段中。 換句話說,這是沒有顯式切換的“翻轉開關”方法。 缺點是客戶需要測試基數約束是否有效; 即約束力弱。
1 - 理論上,您可以實現ListOfTwoOrMore
接口等,但這會帶來一系列新問題。
一種方法是使用不同類型的字段。 現在它具有類型List<String>
,它是一個可以包含0個或更多元素的集合。 您可以將其更改為表示包含2個或更多元素的列表的類型。
您可以在構造函數中使用var-args。
public MyClass(String s1, String s2, String... rest){
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.