最大の問題は、Deckクラスで突然変異のメソッドがあることです。あなたはゲーム中にあなたがデッキに持っているカードを突然変異させることができる必要がありますが、実際にはそれのために実際のデッキオブジェクトを変更する必要はありません。あなたが不変のカードクラスと不変のデッキクラスを持っているとします。デッキは完全なカード補充で初期化されます。あなたがゲームをする必要があるときは、ディーラーが対話、変更などできるカードのリストを尋ねるので、Deckクラスは実際には内部リストのコピーを渡すだけです。そうすれば、ディーラーは内部リストを変更しておらず、要求されたときにデッキは新しいゲームの準備ができており、コピーされたリストには内部デッキクラスと同じカードインスタンスへの参照が保持されていても問題ありません。 。何の状態が共有されていないとデッキは、もはや変更可能であることから
public final class Card {
public enum Suit { HEART, SPADE, CLUB, DIAMOND }
public enum Value { ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING }
private final Suit suit;
private final Value value;
public Card(Suit suit, Value value) {
this.suit = suit;
this.value = value;
}
// ... rest omitted
}
public final class Deck {
private final List<Card> cards = new ArrayList<>();
public Deck() {
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Value value : Card.Value.values()) {
cards.add(new Card(suit, value));
}
}
}
// Get a new list of cards (though the actual card instances are the same
// that's ok since they are immutable)
public List<Card> getCards() {
return new ArrayList<>(cards);
}
}
はまた、スレッドの安全性は、もはや心配はありません。ディーラー用のカードのリストを使用するもう一つの利点は、Collectionsクラスを使用してソートされていること(カード実装のものがあれば)、シャッフルなどです。
編集:あなたのコメントを読んだ後に追加:
あなたはデッキには状態を保持したい場合、あなたはそれが不変であることはできません。これは、さまざまな方法で実装できます。ほとんどの場合、ArrayListは配列に比べてかなり大きなデータ構造ですが、その使用はLinkedListよりパフォーマンスが優れており、安全性と使いやすさの点で優れています。非常に高いレベルのパフォーマンスが必要な場合は、プリミティブ配列(おそらく)でうまくいくことができますが、Collections APIは非常に最適化されており、JVMはコードを最適化する上で非常に優れています。これまでの私の専門的な経験では、単にパフォーマンスのためにリストの上で配列を使用するケースにはまだ踏み込んでいません。一般的に私はそれが時期尚早な最適化のケースであると言います(それに注意してください)。 ArrayListの上にStackを使用するのは抽象的には意味がありますが、Listの組み込みシャッフルメソッドの便利さがCollectionをオーバーライドすると主張します。
次のように上記と同じカードのオブジェクトを使用して、変更可能なデッキは(リストに)になります:
public class EmptyDeckException extends RuntimeException {
static final long serialVersionUID = 0L;
public EmptyDeckException(String message) {
super(message);
}
}
public class Deck {
private final List<Card> cards = new ArrayList<>();
public Deck() {
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Value value : Card.Value.values()) {
cards.add(new Card(suit, value));
}
}
}
public void shuffle() {
Collections.shuffle(cards);
}
public Card pop() {
if (!cards.isEmpty()) {
return cards.remove(cards.size() - 1);
}
throw new EmptyDeckException("The deck is empty");
}
}
とスタックが上主張されている場合、それはこのようなことができます:
あなたのクラスを可変にするカウンタだけでなく、配列が自然に変更可能であるため、リファレンスがfinalでも、カウンタも変更可能です。 –
私は配列のために防御的なコピーを作成します。 –
本当の問題は、あなたが突然変異操作として設計した 'pop'です。指定されたセマンティクスで 'pop'メソッドをサポートする必要がある限り、' Deck'は内部的な手抜きでは不変ではありません。 – user2357112