2011-04-09 51 views
4

我當時正在設計一個用於二十一點遊戲的卡類。每一個不可變的階級應該是最終的嗎?

我的設計是用一個getValue()返回一個Card類,例如,返回J爲11,Q爲12,K爲13,然後用BlackjackCard類擴展它以覆蓋該方法卡片返回10.

然後,有些東西碰到我:Card類的對象應該是不可變的。所以我重新閱讀Effective Java 2nd Edition來看看該做什麼,在那裏我發現不可變類需要是最終的,以避免子類打破不變性。

我也看了看互聯網,每個人似乎都同意這一點。

卡類應該是最終的嗎?

你怎麼能打破這個類的不變性,可以擴展它:

class Card { 
    private final Rank rank; 
    private final Suit suit; 
    public Card(Rank rank, Suit suit) { 
    this.rank = rank; 
    this.suit = suit; 
    } 
    public Rank getRank() { 
    return rank; 
    } 
    public Suit getSuit() { 
    return suit; 
    } 
    public int getValue() { 
    return rank.getValue(); 
    } 
} 

感謝。

回答

1

當他們說不可變的類應該是最終的時,他們指的是如何確保不變性,因爲某些東西是不可變的,它必須是最終的。它是一個小小的區別。如果你不想讓你的課程擴展,那應該是最終的。

2

你可以這樣做:

class MyCard extends Card { 

    public MyCard(Rank rank, Suit suit) { 
    super(rank, suit); 
    } 

    @Override 
    public Rank getRank() { 
    // return whatever Rank you want 
    return null; 
    } 

    @Override 
    public Suit getSuit() { 
    // return whatever Suit you want 
    return null; 
    } 

    @Override 
    public int getValue() { 
    // return whatever value you want 
    return 4711; 
    } 

}

延伸類甚至不具備申報相同的構造函數的父類。它可以有一個默認構造函數,並且不關心父類的最終成員的任何內容。 [該陳述錯誤 - 請參閱評論]。

+1

您的MyCard類無法編譯。它無法訪問'rank'和'suit'。 – 2011-04-09 19:46:32

+0

最後的陳述是不正確的。你必須設置任何最終的成員變量。另外,你的例子不會編譯,因爲你不能重置'this.rank'和'this.suit'。但是你的其他觀點仍然是合法的。 – jtahlborn 2011-04-09 19:46:39

+0

@lwburk&@jtahlborn你是對的 - 最終成員不能被重置 - 輸入這個快速! – FrVaBe 2011-04-09 20:42:07

5

一個子類,實際上不能修改其父private final屬性的值,但它可能表現好像已,這是Effective Java warns against

確保類不能擴展。 這防止粗心或惡意 亞類從由 損害類的不可變 行爲表現得好像該對象的狀態已經改變 。

+0

+1相關的觀點是一個類或其方法不能被擴展,聲明類最終只是幾種方法之一。 – Grundlefleck 2011-06-22 12:03:40

2

答案是肯定的,卡片必須是最終的。

結合K. Claszen和lwburk的反應,看到以下內容:

public class MyCard extends Card { 
    private Rank myRank; 
    private Suit mySuit; 

    public MyCard(Rank rank, Suit suit) { 
     this.myRank = rank; 
     this.mySuit = suit; 
    } 

    @Override public Rank getRank() { return myRank; } 

    public void setRank(Rank rank) { this.myRank = rank; } 

    @Override public Suit getSuit() { return mySuit; } 

    public void setSuit(Suit suit) { this.mySuit = suit; } 

    @Override public int getValue() { return myRank.getValue(); } 
} 

這種擴展將完全忽略父態,並用自己的,可變的狀態替換它。現在在多態上下文中使用Card的類不能依賴它是不可變的。

+0

那麼我該如何編碼BlackjackCard? – gaijinco 2011-04-09 21:15:20

+0

只是讓卡片最終成爲你並且你應該很好...... – 2011-04-09 21:16:51

1

一般來說,這是一個很好的建議。然而,如果你控制了所有的代碼,那麼有時能夠擴展一個不可變的類(可能用其他信息創建另一個不可變類)是有用的。和大多數建議一樣,你必須做出明智的選擇,以確定何時有意義,何時不可以。

1

如果任意代碼可以擴展一個不可變類,那麼任意代碼可以產生類似於不可變類的行爲,但不是不可變的。如果編寫這樣的代碼的人不能損害除自己以外的任何東西,那麼這種情況可能是可以容忍的。如果有人可以使用這樣的班級來繞過安全措施,那麼就不應該容忍它。

請注意,它有時可以有一個類是可擴展的,但它承諾代表所有派生類的不變性。這樣的階級及其消費者當然不得不依賴階級的繼承人來做任何古怪而古怪的事情,但有時候這樣的方法可能仍然比其他任何方法都好。例如,可能有一個班級應該在某個特定時間執行一些動作,但條件是該班級的對象可能是任意的別名。如果這個類,它的任何字段,或者它的任何派生類中的任何字段等都不能使用派生類型,那麼這樣的類就沒有什麼用處,因爲類的定義會限制什麼類型的行爲可能是執行。最好的方法可能是讓該類不被聲明爲final,但在文檔中明確表示可變子類的行爲不會一致或可預測。

1

你可以有一個Valuator是綁遊戲並刪除getValue從類,這樣Card可以final

final class Card { 
    private final Rank rank; 
    private final Suit suit; 
    public Card(Rank rank, Suit suit) { 
    this.rank = rank; 
    this.suit = suit; 
    } 
    public Rank getRank() { 
    return rank; 
    } 
    public Suit getSuit() { 
    return suit; 
    } 
} 

與評價者的工作是這樣的:

interface CardValuator { 
    int getValue(Card card); 
} 

class StandardValuator implements CardValuator { 
    @Override 
    public int getValue(Card card) { 
    return card.getRank().getValue(); 
    } 
} 

class BlackjackValuator implements CardValuator { 
    @Override 
    public int getValue(Card card) { 
    ... 
    } 
} 

現在,如果你仍然想保持你的Card層次結構,標記Card方法final會阻止子類重寫它們。

相關問題