オブジェクト指向設計がようやく分かり始めてきた

公開日:2019-07-23
最終更新:2019-08-06

※以前はQiitaで記事を書いていました。(マイページのリンク)

オブジェクト指向設計を勉強して

BlackJackゲームをJava初心者卒業の課題として作製しよう!ということで、作ってました。
その進捗を上述したQiitaに記事として上げてました。

その記事に付いたコメントの中で、「いずれオブジェクト指向設計ができるようになればよいですね」という話があったので、オブジェクト指向について勉強してました。

最終的にオブジェクト指向設計でBlackJackゲームを作製しなおせたので、ここにアウトプットとして置いておきます。
Java勉強し始めて二ヶ月目なんで大目に見たってくだせえ

ソースコード

Main.java

最初はBlackJack.game();でゲームをスタートさせようと思ったんですが、BlackJack.javaの中で複数人でプレイすることに対応させようとしたために、staticを外したので、インスタンスを一旦作ってメソッドを実行させる形になりました。(スマートな方法があればよいのですが…)

public class Main {  
    public static void main(String[] args) {  
        BlackJack blackJack = new BlackJack();  
        blackJack.game();  
    }  
}

BlackJack.java

ゲームマスター(≠ディーラー)視点でゲームを進めていくためのクラスです。

import java.util.Scanner;  
import java.util.List;  
import java.util.ArrayList;  
public class BlackJack {  
    private Scanner scanner = new Scanner(System.in);  
    // 何人プレイするかを保持するリスト  
    private List&ltString> humans = new ArrayList();  
    // 実際のインスタンスを保持するリスト  
    private List&ltPlayer> players = new ArrayList();  
    public void game() {      
        // 複数人プレイに対応  
        countPlayer();      
        // 山札インスタンスとディーラーインスタンスの生成  
        Deck deck = new Deck();  
        Dealer dealer = new Dealer("ディーラー", deck);  
        // 人数分インスタンスを生成  
        for (int itr = 0; itr < humans.size(); itr++){  
            Player player = new Player(humans.get(itr));  
            players.add(player);              
        }  
        System.out.println("");  
        // ディーラーの初期手札  
        dealer.firstDealToDealer();  
        // 人数分初期手札を配る  
        for (int itr = 0; itr < players.size(); itr++){  
            dealer.firstDealToPlayer(players.get(itr));  
        }  
        // プレイヤーたちの番  
        for (int itr = 0;itr < players.size(); itr++){  
            players.get(itr).play(dealer);  
            if(itr == players.size() - 1) {  
                System.out.println("他にプレイヤーが残っていないため、ディーラーの番になります");  
            }  
        }  
        // ディーラーの番  
        dealer.play();  
        // 判定  
        for (int itr = 0; itr < players.size(); itr++) {  
            dealer.judge(players.get(itr), dealer);  
        }  
    }  
    public void countPlayer() {  
        System.out.print("プレイする人数を入力してください>");  
        int human = scanner.nextInt();  
        for (int itr = 0; itr < human; itr++){  
            System.out.print("プレイする人の名前を一人ずつ入力してください>");  
            String name = scanner.next();  
            humans.add(name);  
        }  
    }  
}

Human.java

PlayerクラスとDealerクラスの元となる抽象クラスです。

import java.util.ArrayList;  
public abstract class Human {  
    // 名前を持っている  
    private String name;  
    // 手札を持っている  
    private Hand hand;  
    // バーストしているかどうか  
    private boolean bust;  
    public Human(String name) {  
        this.name = name;  
        this.hand = new Hand();  
    }  
    // toString  
    public String toString() {  
        return this.name;  
    }  
    public String getName() {  
        return this.name;  
    }  
    public Hand getHand() {  
        return this.hand;  
    }  
    public boolean getBust() {  
        return this.bust;  
    }  
    public void setBust(boolean check) {  
        this.bust = check;  
    }  
    // カードを引くかどうか宣言する  
    public abstract boolean askToHit();  
    // カードを引く  
    public void draw(Card card) {  
        hand.add(card);  
    }  
    // 手札とポイントの開示  
    public void showHave() {  
        message(getName() + " の手札は " + getHand().toString() + " です");  
        message(getName() + " の得点は " + calcScore() + " 点です");  
    }  
    // ポイント計算  
    public int calcScore() {  
        int score = hand.getPoints();  
        return score;  
    }  
    // バーストしてるかの計算  
    public boolean isBust() {  
        if (calcScore() > 21){  
            return true;  
        } else {  
            return false;  
        }  
    }  
}

Player.java

実際に人が操作するためのプレイヤークラスです。

import java.util.Scanner;  
public class Player extends Human {  
    private Scanner scanner = new Scanner(System.in);  
    public Player(String name) {  
        super(name);  
    }      
    public boolean askToHit() {  
        while(!isBust()) {  
            message(this.getName() + "の番です。");  
            showHave();  
            message("ヒットしますか?する場合はYを、しない場合はNを入力してください");  
            String ans = scanner.next();  
            if (ans.equals("Y")) {  
                return true;  
            } else if (ans.equals("N")){  
                return false;  
            } else {  
                message("Y/N以外が入力されました");  
                continue;  
            }  
        }   
        return false;  
    }  
    public void play(Dealer dealer) {  
        while(true) {  
            if (askToHit()) {  
                    // Hitの場合  
                    dealer.dealCard(this);  
                    message("");  
                } else if(isBust()){  
                    // Burstした場合  
                    showHave();  
                    setBust(true);  
                    message(this.getName() + "はバストしました");  
                    message("次のプレイヤーに番が回ります。");  
                    message("");  
                    break;  
                } else {  
                    // Standの場合  
                    setBust(false);  
                    message(this.getName() + "はStandしました");  
                    message("次のプレイヤーに番が回ります");  
                    message("");  
                    break;  
                }  
        }  
    }  
}

Dealer.java

CPU側であるディーラーのクラスです。山札を持たせてカード配ったり勝利判定をしてもらってます。

public class Dealer extends Human {  
    private Deck bills = new Deck();  
    public Dealer(String name, Deck deck) {  
        super(name);  
        this.bills = deck;  
    }  
    public void firstDealToDealer() {  
        // ディーラーにカードを2枚配る  
        dealFirstCard(this);  
        dealFirstCard(this);  
        // ディーラーのカードは1枚だけ表示  
        message(this.getName() + " の手札は " + this.getHand().firstCard() + " ともう一枚は内緒");  
        message("");  
    }  
    public void firstDealToPlayer(Player player) {  
        dealFirstCard(player);  
        dealFirstCard(player);  
        player.showHave();  
        message("");  
    }  
    public void dealFirstCard(Human player) {  
        Card card = bills.getTop();  
        player.draw(card);  
        bills.remove();  
    }  
    public void dealCard(Human player) {  
        Card card = bills.getTop();  
        player.draw(card);  
        message("引いたカードは " + card.toString() + " です");  
        bills.remove();  
    }  
    public boolean askToHit() {  
        return calcScore() < 17;  
    }  
    public void play() {  
        while(true) {  
            this.showHave();  
            if (askToHit()) {  
                dealCard(this);  
                message("");  
            } else if(isBust()) {  
                // Burstしたとき  
                setBust(true);  
                message(this.getName() + "がバストしました。");  
                message("");  
                break;  
            } else {  
                // 17以上になったとき  
                setBust(false);  
                message(this.getName() + "がStandしました。判定に移ります。");  
                message("");  
                break;  
            }  
        }  
    }  
    public void judge(Player player, Dealer dealer) {  
        message("--------" + player.getName() + " vs " + dealer.getName() + "--------");  
        if (!dealer.getBust()) {  
            if (!player.getBust()) {  
                if (player.calcScore() >= dealer.calcScore()) {  
                    message(player.getName() + "の勝利です。");  
                } else {  
                    message(dealer.getName() + "の勝利です。");  
                }  
            } else {  
                message(dealer.getName() + "の勝利です。");  
            }  
        } else if (!player.getBust()){  
            message(player.getName() + "の勝利です");  
        } else {  
            message(dealer.getName() + "の勝利です");  
        }  
        message("また遊んでね!");  
        message("");  
    }  
}

Deck.java

ディーラーが持つトランプの山札を表すクラスです。for文のi < 13はA~Kの割り振りのためです。

import java.util.List;  
import java.util.Collections;  
import java.util.ArrayList;  
public class Deck {  
  //山札をリストで記録  
    private List&ltCard> bill = new ArrayList();  
    public Deck() {  
        for (Suit suit : Suit.values()) {  
            for (int i = 0; i < 13; i++){  
                Card card = new Card(suit, i + 1);  
                bill.add(card);  
            }  
        }  
        Collections.shuffle(bill);  
    }  
    public Card getTop() {  
        return bill.get(0);  
    }  
    public void remove(){  
        bill.remove(0);  
    }  
}

Hand.java

プレイヤーがそれぞれ持ってる手札を表すクラスです。hasAceメソッドでAが手札にあるかどうかを判定し、getPointsメソッドで1点と計算するか11点と計算するかに利用してます。

import java.util.List;  
import java.util.ArrayList;  
public class Hand {  
    private List hands = new ArrayList();  
    private int points;  
    public void add(Card card) {  
        hands.add(card);  
    }  
    public List getHand() {  
        return this.hands;  
    }  
    public String toString() {  
        String handList = "";  
        for (int itr = 0; itr < hands.size(); itr++) {  
            if (itr == hands.size() - 1) {  
                handList += hands.get(itr);  
            } else {  
                handList += hands.get(itr) + " と ";  
            }  
        }  
        return handList;  
    }  
    public int getPoints() {  
        points = 0;  
        for (int itr = 0; itr < hands.size(); itr++) {  
            points += hands.get(itr).getPoint();  
        }  
        if (hasAce() && points <=11) {  
            points += 10;  
        }  
        return points;  
    }  
    public boolean hasAce() {  
        boolean check = false;  
        for (int itr = 0; itr < hands.size(); itr++) {  
            if (hands.get(itr).getRank() == 1) {  
                check = true;  
            }  
        }   
        return check;  
    }  
    public Card firstCard() {  
        return hands.get(0);  
    }  
}

Card.java

トランプ一枚を表すクラスです。

public class Card {  
    private Suit suit;  
    private int rank;  
    private String toDisplayValue() {  
        switch (this.rank) {  
            case 1:  
                return "A";  
            case 11:  
                return "J";  
            case 12:  
                return "Q";  
            case 13:  
                return "K";  
            default:  
                return String.valueOf(this.rank);  
        }  
    }  
    public Card(Suit suit, int rank) {  
        this.suit = suit;  
        this.rank = rank;  
    }  
    public int getRank() {  
        return this.rank;  
    }      
    public int getPoint() {  
        if (this.rank > 10) {  
            return 10;  
        } else {  
            return this.rank;  
        }  
    }  
    public String toString() {  
        return suit.getMark() + "の" + toDisplayValue();  
    }  
}

Suit.java

カードの記号をenumから取ってくるために利用してます。

public enum Suit {  
    SPADE("スペード"),  
    CLUB("クラブ"),  
    HEART("ハート"),  
    DIAMOND("ダイヤ");  
    private String mark;  
    Suit(String mark) {  
        this.mark = mark;  
    }  
    public String getMark() {  
        return this.mark;  
    }  
}

感想

滅茶苦茶長くなってしまった…

でも複数人でプレイできるようにはなったし、いっか!
またGitHubにアップしたり、リファクタリングしたら追記します。
「ここ気持ち悪いから直せ!」とか「ここ要らない!」とかありましたらコメントください。

----(7.24 追記)----

パッケージ分けとか要らない記述を消したり色々したので、変更かけたやつをGitHubに上げます(予定)

----(8.6 追記)----

めんどくさがってやってなかったんですが、ようやくGitHubにソースコードアップしました。

GitHub:DoroRitch/OOD

記事が少しでもいいなと思ったらクラップを送ってみよう!
0
+1
@DoroRitchの技術ブログ

よく一緒に読まれている記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる

技術ブログをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

技術ブログを開設する

Qrunchでアウトプットをはじめよう

Qrunch(クランチ)は、ITエンジニアリングに携わる全ての人のための技術ブログプラットフォームです。

Markdownで書ける

ログ機能でアウトプットを加速

デザインのカスタマイズが可能

技術ブログ開設

ここから先はアカウント(ブログ)開設が必要です

英数字4文字以上
.qrunch.io
英数字6文字以上
ログインする