Refactoring: faire du Blackjack en Java

Post-scriptum: @ k73i55no5 a commenté une meilleure proposition de refactoring. Merci beaucoup. Voir les commentaires pour plus de détails. Refactoriser uniquement la structure des membres de la classe (structure). Nous n'avons pas commencé à refactoriser le code de traitement (intérieur, implémentation). (Ajouté jusqu'à ici)

J'ai vu le code source de "Make Blackjack with Java" posté par @ yuta-yoshinaga. J'ai écrit un commentaire un peu amer parce que l'encapsulation a été détruite par tous les setters et getters, et certaines classes ne fonctionnaient pas. C'est juste un commentaire, alors je l'ai refactorisé à ma manière. Il y a encore quelques points à revoir, mais j'espère que vous le trouverez utile.

Ajoutez un diagramme de séquence.

@startuml
participant "BlackJack" as blackjack
@enduml

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 "???";
        }
    }
}

Recommended Posts

Refactoring: faire du Blackjack en Java
Faites un blackjack avec Java
Facile à créer Slack Bot avec Java
Partition en Java
Changements dans Java 11
Janken à Java
Refactoring dans JUnit
Taux circonférentiel à Java
FizzBuzz en Java
Je voulais que (a == 1 && a == 2 && a == 3) vrai en Java
Implémentation de l'interpréteur par Java
Application Janken en Java
Programmation par contraintes en Java
Mettez java8 dans centos7
NVL-ish guy en Java
"Hello World" en Java
Interface appelable en Java
Commentaires dans la source Java
Fonctions Azure en Java
Formater XML en Java
Simple htmlspecialchars en Java
Implémentation Boyer-Moore en Java
Hello World en Java
Utiliser OpenCV avec Java
Mémorandum WebApi avec Java
Détermination de type en Java
Exécuter des commandes en Java (ping)
Divers threads en java
Implémentation du tri de tas (en java)
API Zabbix en Java
Art ASCII à Java
Comparer des listes en Java
POST JSON en Java
Exprimer l'échec en Java
Créer JSON en Java
Manipulation de la date dans Java 8
Nouveautés de Java 8
Utiliser PreparedStatement en Java
Nouveautés de Java 9,10,11
Exécution parallèle en Java
Les débutants en Java créent des parties de poker en 4 jours (3ème jour)
[Mémo personnel] Créez une copie complète simple avec Java
J'ai essayé de créer une fonction de connexion avec Java
Les débutants en Java créent des parties de poker en 4 jours (2ème jour)
Essayez d'utiliser RocksDB avec Java
[Java] Rendre les variables de l'instruction for étendue et de chaque instruction immuables
Lire des fichiers binaires en Java 1
Évitez l'erreur que Yuma a donnée en Java
Faisons une application de calcul avec Java ~ Afficher la fenêtre de l'application
Obtenir des informations EXIF en Java
Java - Comment créer JTable
[Neta] Sleep Sort en Java
Modifier ini en Java: ini4j
L'histoire de Java dans ce monde
Segfo Java en 6 lignes
Essayez de développer Spresense avec Java (1)
Essayez le type fonctionnel en Java! ①
J'ai fait une roulette à Java.