2012-08-04 49 views
9
延長不變類型

我已經開始在Java中不可變值對象玩弄一個遊戲項目時,遵循「公開的最終場」的方法:如何在Java中

public class Team { 
    public final String name, flag; 

    public Team(String name, String flag) { 
     this.name = name; 
     this.flag = flag; 
    } 
} 

這工作很適合我到目前爲止,但在不同情況下我需要不同的關於團隊的額外信息。例如,一支球隊在比賽中擁有一套顏色。問題是,處理這些擴展信息的最佳方式是什麼?我知道這是一個相當普遍的問題,但我想繼續使用不可變的對象,這可能會影響解決方案。

下面是我想出的選項。他們中的大多數可能「足夠好」,但我想學習一些贊成和反對他們的論點供將來參考。

選項1:一類

public class Team { 
    public final String name, flag, colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     this.name = name; 
     this.flag = flag; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

這需要只有一個所有用途類,但有什麼額外的數據,預計/提供的基於類型的指示一切。

選項2:子類

public class TeamWithColor extends Team { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String name, String flag, String colorName, int colorRgb) { 
     super(name, flag); 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

這可能讓一個基於內容的equals()方法的實現是不可能的。

方案3:組成

public class TeamWithColor { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

較少複製/樣板代碼,如果球隊數據和額外的數據往往是獨立發生變化。

選項4:對/元組(使用一個不變的Pair類)

public class TeamColor { 
    public final String colorName; 
    public final int colorRgb; 

    public Team(String colorName, int colorRgb) { 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

Pair<Team, TeamColor> teamWithColor = Pair.create(team, teamColor); 

...或者是關係團隊和TeamColor在一起的自定義類。

我趨向於選擇3或4,但我感興趣的是你的觀點,論據和直覺感受:)

+1

或者使用*接口*(例如'ITeam','ITeamColor')..構造函數不能無論如何統一。 – 2012-08-04 17:02:24

+2

如果你所有的球隊都有顏色,那麼它應該全部在一個班級中。 – Strelok 2012-08-04 17:15:19

+0

這不是一個真正的答案,但我會說,你應該讓客戶端驅動器的接口設計(這是使用'Team'對象和顏色的代碼)。也就是說,先寫客戶端,然後把模型寫出來。然後使用你學到的東西來完善模型並完成它的實現。 – erickson 2012-08-04 18:31:46

回答

6

至於你說:

創建不變類推薦的最佳實踐。該團隊可能出現在不同的情況下。 這些情況是爲團隊提供附加屬性的上下文。

因此,我建議爲添加數據的每個不同上下文使用組合。

public class TeamWithColor { 
    public final Team team; 
    public final TeamColor teamColor; 

    public Team(Team team, TeamColor teamColor) { 
     this.team = team; 
     this.teamColor = teamColor; 
    } 
} 

也許你就會有:

public class TeamDuringOlimpics{ 
    public final Team team; 
    public final TeamColor teamColor; 
    public final TeamFlag teamFlag; 

    public Team(Team team, TeamColor teamColor, TeamFlag teamFlagTeamFlag teamFlag) { 
     this.team = team; 
     this.teamColor = teamColor; 
     this.teamFlag = teamFlag; 
    }  
} 
2

組成聽起來像並稱需要是可變的上下文數據一個不錯的選擇。

在Java中,不可變類通常標記爲final,不能擴展。以String爲例。這排除了選項編號2.

厭倦使用對。 Pair類型尚未添加到Java中有很多很好的理由。在這種情況下,您的數據通過創建新的數據類型(即直通組合)更好地建模。 http://www.javapractices.com/topic/TopicAction.do?Id=29

+0

好處是,如果您宣傳一個類爲不可變,則確保不能有可變子類,儘管在這種情況下唯一可能會出現意外行爲的是來自Object的方法。但是我問的是一個迂腐的問題,所以我應該得到迂迴的答案:P – Medo42 2012-08-04 17:31:08

0

你可以考慮保持這個資料片的團隊對象之外。例如,你可以有地圖或

通過這種方式,你可以充實了許多額外的價值你的對象,而不會引入新的類或修改現有的類。此外,它會保持其他對象的不變屬性。

1

如果一個不可變類是否可繼承,或者包含其類型可以是可變的方面,那麼它將對惡作劇沒有安全。 「不可變」的界面同樣沒有安全性。同樣,如果對象具有可擴展類型的任何成員,並且對象被認爲包含這些成員引用的對象中的任何信息。這取決於你是否有問題。不可變的接口和可擴展的可變類可能比深度封閉的類多得多;如果一個班級不會被深度封閉,那麼在部分情況下幾乎沒有安全優勢。

請注意,這是可能的根深蒂固的不可變對象有一個字段是一個可變型的,如果該字段的語義指定它標識,而不是包含,所討論的對象。例如,它可以是具有有一個「快照」對象,其包含引用一些可變對象,並且對於每一個對象,其當前狀態的不可變拷貝有用;那麼「快照」對象就會公開一種將每個對象恢復到生成快照時的狀態的方法。雖然快照對象引用了一個可變對象,但該對象將是「深度不可變的」,因爲快照對可變對象的引用純粹是爲了標識它們,並且這些對象的身份即使其狀態確實也不會改變。

我喜歡.net中的一種模式,它也應該可以在Java中使用,它的接口類似於IReadableFooIImmutableFoo;後者會繼承前者,但不會添加任何新成員。接口IReadableFoo應該包含,除了成員閱讀它的屬性,成員AsImmutable()這將返回一個IImmutableFoo。這種方法允許代碼在方便時使用可變類型,同時最小化多餘的防禦性複製(代碼中想要保留的東西必須調用AsImmutable();如果所討論的對象是可變的,那麼該動作將創建不可變的副本,但是如果對象是已經是不可變的,不需要新的副本)。

0

我建議Decorator模式。

public class TeamWithColor extends Team { 
    public final Team team; 
    public final String colorName; 
    public final int colorRgb; 

    public Team(Team team, String colorName, int colorRgb) { 
     this.team = team; 
     this.colorName = colorName; 
     this.colorRgb = colorRgb; 
    } 
} 

http://en.wikipedia.org/wiki/Decorator_pattern

+1

這不是裝飾者 - 爲此,「團隊」必須是一個接口。如果我們轉而使用getter和interfaces,裝飾器可能會將組合的優點(較少複製)與繼承的優點結合起來(可以在任何地方使用TeamWithColor'可以使用Team)。不利的一面是,你必須將委託獲取器放在'TeamWithColor'中,所以你不能在沒有編輯'TeamWithColor'的情況下將字段添加到'Team'。另外,它需要更多的代碼(兩個接口,兩個類,許多獲取者)。 – Medo42 2012-08-07 07:41:24