2011-04-09 19 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会阻止子类重写它们。

相关问题