LoginSignup
9
13

More than 1 year has passed since last update.

リファクタリング: Javaでブラックジャックを作る

Last updated at Posted at 2019-04-28

追記:
@k73i55no5さんより、より良いリファクタリング案をコメントいただきました。ありがとうございます。詳しくはコメントをご覧下さい。
クラス・メンバ構成(構造)のリファクタリングだけしています。
処理コード(内装、実装)のリファクタリングには着手していません。
(ここまで追記)

@yuta-yoshinaga さんが投稿された「Javaでブラックジャックを作る」のソースコードを拝見しました。
セッター・ゲッターだらけでカプセル化が破壊されていて、仕事をしていないクラスもあり、ちょっと辛辣なコメントを書いてしまいました。
コメントだけでは何なので、私なりにリファクタリングしてみました。
まだ見直す個所はありますが、何か参考になることがあれば幸いです。

クラス図:

BlackJack.java
public class BlackJack {
    private final Player player;
    private final Dealer dealer;

    public BlackJack() {
        this(new CUIPlayer(), new Dealer());
    }

    public BlackJack(Player player, Dealer dealer) {
        this.player = player;
        this.dealer = dealer;
    }

    public void play() {
        player.reset();
        dealer.reset();
        for (int i = 0; i < 2; i++) {
            dealer.dealCard(player);
            dealer.dealCard(dealer);
        }
        dealer.show();
        if (player.play(dealer)) {
            dealer.play(dealer);
            showJudge();
        }
    }

    public void showJudge() {
        dealer.show();
        player.show();
        Player winner = judgeWinner();
        System.out.println("----------");
        if (winner == player) {
            System.out.println("You are the winner.");
        } else if (winner == dealer) {
            System.out.println("It is your loss.");
        } else {
            System.out.println("It is a draw.");
        }
    }

    public Player judgeWinner() {
        if (player.isBust()) {
            return dealer;
        } else  if (dealer.isBust()) {
            return player;
        } else if (player.isBlackJack() && !dealer.isBlackJack()) {
            return player;
        } else if (dealer.isBlackJack() && !player.isBlackJack()) {
            return dealer;
        }
        int diff = player.calcScore() - dealer.calcScore();
        if (diff > 0) {
            return player;
        } else if (diff < 0) {
            return dealer;
        } else {
            return null;
        }
    }

    public static void main(String[] args) {
        BlackJack blackjack = new BlackJack();
        while (true) {
            blackjack.play();
            blackjack.player.play(null);
        }
    }
}
Player.java
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

abstract public class Player {
    public final String name;
    protected final List<Card> cards = new ArrayList<Card>();
    protected boolean stand = false;

    public Player() {
        this("Player");
    }

    public Player(String name) {
        this.name = name;
    }

    public void reset() {
        cards.clear();
        stand = false;
    }

    public void holdCard(Card card) {
        cards.add(card);
    }

    abstract boolean play(CardDealer dealer);

    public int calcScore() {
        int score = 0;
        boolean hasAce = false;
        for (Card card: cards) {
            score += card.rank < 10 ? card.rank : 10;
            if (card.rank == 1) {
                hasAce = true;
            }
        }
        if (score <= 11 && hasAce) {
            score += 10;
        }
        return score;
    }

    public boolean isBlackJack() {
        return cards.size() == 2 && calcScore() == 21;
    }

    public boolean isBust() {
        return calcScore() > 21;
    }

    public void show() {
        System.out.println("----------");
        showCards();
        System.out.println(name + "'s score: " + calcScore());
    }

    public void showCards() {
        System.out.println(name + "'s card: " + cards.stream().map(Object::toString).collect(Collectors.joining(", ")));
    }
}
CUIPlayer.java
import java.util.Scanner;

public class CUIPlayer extends Player {
    private Scanner sc = new Scanner(System.in);

    public boolean play(CardDealer dealer) {
        while (!isBust() || dealer == null) {
            if (!stand) {
                show();
            }
            System.out.println("----------");
            System.out.println("Please enter a command.");
            System.out.println("  q: quit");
            System.out.println("  r: restart");
            if (!stand && dealer != null) {
                System.out.println("  h: hit");
                System.out.println("  s: stand");
            }
            System.out.print("? ");
            String inputStr = sc.nextLine();
            switch (inputStr) {
                case "q":
                case "quit":
                    System.out.println("bye.");
                    sc.close();
                    System.exit(0);
                    break;
                case "r":
                case "reset":
                    return false;
                case "h":
                case "hit":
                    if (!stand && dealer != null) {
                        dealer.dealCard(this);
                    }
                    break;
                case "s":
                case "stand":
                    stand = true;
                    return true;
                default:
                    System.out.println("Unsupported command.");
                    break;
            }
        }
        stand = true;
        return true;
    }
}
Dealer.java
public class Dealer extends Player implements CardDealer {
    private final Deck deck;

    public Dealer() {
        this(new Deck());
    }

    public Dealer(Deck deck) {
        super("Dealer");
        this.deck = deck;
    }

    public void dealCard(Player player) {
        player.holdCard(deck.drowCard());
    }

    public boolean play(CardDealer dealer) {
        while (calcScore() < 17) {
            dealer.dealCard(this);
        }
        stand = true;
        return true;
    }

    @Override
    public void show() {
        if (stand || cards.size() != 2) {
            super.show();
        } else {
            System.out.println("----------");
            cards.get(1).faceDown();
            showCards();
            cards.get(1).faceUp();
        }
    }
}
CardDealer.java
public interface CardDealer {
    public void dealCard(Player player);
}
Deck.java
import java.util.ArrayList;
import java.util.Collections;

public class Deck {
    private final ArrayList<Card> cards = new ArrayList<Card>();

    public Deck() {
        reset();
    }

    void reset() {
        cards.clear();
        for (Suit suit: Suit.values()) {
            for (int rank = 1; rank <= 13; rank++) {
                cards.add(new Card(suit, rank));
            }
        }
        shuffle();
    }

    public void shuffle() {
        Collections.shuffle(cards);
    }

    public Card drowCard() {
        if (cards.size() == 0) {
            reset();
        }
        return cards.remove(0);
    }
}
Card.java
enum Suit {
    SPADE, CLOBBER, HEART, DIAMOND;
}

public class Card {
    public static String[] RANK = {
        "", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"
    };

    public final Suit suit;
    public final int rank;
    private boolean visible;

    public Card(Suit suit, int rank) {
        this.suit = suit;
        this.rank = rank;
        faceUp();
    }

    public void faceUp() {
        this.visible = true;
    }

    public void faceDown() {
        this.visible = false;
    }

    @Override
    public String toString() {
        if (visible) {
            return suit.name() + ' ' + RANK[rank];
        } else {
            return "???";
        }
    }
}
9
13
15

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
13