ブラックジャックゲーム分析-1


前の文章では、ブラックジャックゲームを再開したいと思っていました.
このプロジェクトはウトコ2期無料コースで行われたプロジェクトです.
完全なコードを確認できます.
このコードはkouz95のコードに基づいています.
今回は包装構造と各等級の構成を一つ一つ分解します.
羊がぶるぶる震えるだろう...ㅠ?

需要分析



パッケージ分析

  • domain
  • card
  • Card.class
  • CardRepository.class
  • Cards.class
  • Deck.class
  • Symbol.enum
  • Type.enum
  • controller
  • blackjackController.class
  • user
  • strategy.draw
  • DealerDrawStrategy.class
  • PlayerDrawStrategy.class
  • DrawStrategy.interface
  • Dealer.class
  • Player.class
  • User.interface
  • Users.class
  • BettingMoney.class
  • PlayerIntentionType.enum
  • PlayerMoneys.class
  • ResultType.enum
  • ScoreType.enum
  • view
  • InputView
  • OutputView
  • 呜呜...
    これこそ単一の責任の終結者である.
    今まで、一つの項目をバイト単位で分けたいと思っていました.
    私のような雌鶏はすべてのレベルの息吹を完全に感じなければならない.
    スアムガボズア.

    ぶんせき


    Card.class
    public class Card {
        private final Symbol symbol;
        private final Type type;
    
        public Card(Symbol symbol, Type type) {
            this.symbol = symbol;
            this.type = type;
        }
    
        public Symbol getSymbol() {
            return symbol;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Card card = (Card) o;
            return symbol == card.symbol && type == card.type;
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(symbol, type);
        }
    
        @Override
        public String toString() {
            return symbol.getName() + type.getName();
        }
    }
    エンumと発表されたSymbolとTypeからなるブラックジャックゲームのコアオブジェクトカードです.
    特に場所はありませんが、equals()とhashCodeのカバーが印象的でした.あなたは意外にもこのように極端にoopに追随します.
    Symbol.enum
    public enum Symbol {
        ACE(1, "A", score -> score <= 11, () -> 10),
        TWO(2, "2"),
        THREE(3, "3"),
        FOUR(4, "4"),
        FIVE(5, "5"),
        SIX(6, "6"),
        SEVEN(7, "7"),
        EIGHT(8, "8"),
        NINE(9, "9"),
        TEN(10, "10"),
        JACK(10, "J"),
        QUEEN(10, "Q"),
        KING(10, "K");
    
        private final int score;
        private final String name;
        private final Predicate<Integer> promotionJudge;
        private final Supplier<Integer> bonusScore;
    
        Symbol(int score, String name) {
            this(score, name, i -> false, () -> 0);
        }
    
        Symbol(int score, String name, Predicate<Integer> promotionJudge, Supplier<Integer> bonusScore) {
            this.score = score;
            this.name = name;
            this.promotionJudge = promotionJudge;
            this.bonusScore = bonusScore;
        }
    
        public int getPoint() {
            return score;
        }
    
        public String getName() {
            return name;
        }
    
        public boolean isPromotable(int score) {
            return promotionJudge.test(score);
        }
    
        public int getBonusPoint() {
            return bonusScore.get();
        }
    }
    まず注意したいのはPredicatとSupplierの使用です
    前の最近の投稿はすべてここのために構築されました...!
    まずジェネレータを見てみましょう
    生成者には2種類ある
    Symbol(int score, String name)
    Symbol(int score, String name, Predicate<Integer> promotionJudge, Supplier<Integer> bonusScore)
    この数字から見ると、ACEにはPredicateとSupplierがあり、他の属性はありません.
    まずACEを分析してみましょうACE(1, "A", score -> score <= 11, () -> 10)2番目のプロパティPredicatは、一定の条件でパラメータをチェックして論理値を返します.
    IntegerのパラメータScoreを調べ、score <= 11の結果を返します.
    3番目のプロパティSupplierは、リクエスト時に関数の特定の値を返すプロバイダです.
    Supplier.get()が呼び出されると、() -> 10を介して10が返されます.
    もしそうであれば、この2つの属性がない値に対して:
    Symbol(int score, String name) {
        this(score, name, i -> false, () -> 0);
    }
    このジェネレータに乗ると、
    Predicatの場合、パラメータiを入力すると、インスタンスは定数falseのram式を返します.
    Supplierは、呼び出し時に0を返すインスタンスを持つEnum値でもあります.
    CardRepository.class
    public class CardRepository {
        private static final List<Card> cards;
    
        static {
            cards = Arrays.stream(Symbol.values())
                    .flatMap(CardRepository::mapToCardByType)
                    .collect(Collectors.toList());
        }
    
        private static Stream<Card> mapToCardByType(Symbol symbol) {
            return Arrays.stream(Type.values())
                    .map(type -> new Card(symbol, type));
        }
    
        public static List<Card> toList() {
            return Collections.unmodifiableList(cards);
        }
    }
    まず...
    モデルでデータベースへのアクセス方法を使用するインタフェース...
    このJavaプロジェクトでは、Repositoryがどのように使用されているかを理解してこそ、その役割を本当に理解することができます.
    まず,クラス初期化ブロックにはCardの1次集合が定義されている.
    これまでブラックジャックゲームで苦悩していたenumの2重砲口文が方法で分離され、簡潔に1深に処理された姿が印象的でした.
    方法では、外側ループはFlatMapを使用し、内側ループはmapを使用する.
  • map:単一ストリーム内の要素を所望の特定の形状に変換できます.
  • flatMap:すべての要素を単一の要素ストリームに戻すことができます.
  • map()メソッドとflatMap()メソッドは両方とも中間ストリームで動作し,他のストリームをメソッド出力に戻す.
    map()とflatMap()の主な違いは,2つの方法の戻りタイプである.
    map()オブジェクトストリームが存在する場合、演算を使用して、ストリーム内の各要素に一意の値を取得する必要があります.
    1対1のI/O要素間のマッピング.
    たとえば、すべての従業員の生年月日を従業員フローで検索するプログラムを作成できます.
    FlatMap()の場合、入力要素/ストリームごとに1対のマルチマッピングを作成し、最初に複数の値を取得してから、すべての入力ストリームの値を単一の出力ストリームにマージします.
    たとえば、テキストファイルの各行ですべてのグローバル単語を検索するプログラムを作成できます.
    Java Stream map()とflatMap()の違い
    まずここを見たとき
    Repositoryの役割は,カードオブジェクトを一次集合に包装して生成し,実際のデータにすることであるようである.
    Cards.class
    public class Cards {
        private static final int INITIAL_CARDS_SIZE = 2;
    
        private final List<Card> cards;
    
        public Cards(List<Card> cards) {
            this.cards = cards;
        }
    
        public void add(Card card) {
            cards.add(card);
        }
    
        public boolean isInitialSize() {
            return cards.size() == INITIAL_CARDS_SIZE;
        }
    
        public boolean isNotInitialSize() {
            return cards.size() != INITIAL_CARDS_SIZE;
        }
    
        public int getPoint() {
            int point = cards.stream()
                    .map(Card::getSymbol)
                    .mapToInt(Symbol::getPoint)
                    .sum();
    
            int bonusPoint = cards.stream()
                    .map(Card::getSymbol)
                    .filter(symbol -> symbol.isPromotable(point))
                    .mapToInt(Symbol::getBonusPoint)
                    .findFirst()
                    .orElse(0);
    
            int resultPoint = point + bonusPoint;
            return ScoreType.of(resultPoint).getScore(resultPoint);
        }
    
        public List<Card> toList() {
            return Collections.unmodifiableList(cards);
        }
    }
    このオブジェクトをカードの一級集合と誤認する可能性があるが,そのオブジェクトの役割はディーラーとプレイヤーの手を担当する小さなカードリストである.
    スコア計算と長寿カウントを担当していますが、印象的な部分は
    これはisInitialSize(), isNotInitialSize()メソッドです.
    一つの方法だ!否定すればいいのですが、毒性のために単独で取り除いたようで、
    これがいいかどうか考えなければなりません.
    Deck.class
    public class Deck {
        private final Stack<Card> deck;
    
        private Deck(Stack<Card> deck) {
            this.deck = deck;
        }
    
        public static Deck of(List<Card> cards) {
            Stack<Card> deck = new Stack<>();
            cards.forEach(deck::push);
            Collections.shuffle(deck);
            return new Deck(deck);
        }
    
        public Card pop() {
            return deck.pop();
        }
    }
    これはゲームを行うカードのインデックスです.
    一級集合Cardsでは,演算のためにStackに変換する.
    簡単ですが、簡潔なコードが印象的で、プロジェクトではof()メソッドがよく登場しますが、どういう意味でのofネーミングなのか知りたいです.
    構成の意味ですか?
    User.interface
    public abstract class User {
        private static final int BLACKJACK_SCORE = 21;
        private static final int INITIAL_HANDS_SIZE = 2;
    
        protected Cards hands;
        protected DrawStrategy drawStrategy;
    
        protected User() {
            this.hands = new Cards(new ArrayList<>());
        }
    
        public void proceedInitialPhase(Deck deck) {
            for (int i = 0; i < INITIAL_HANDS_SIZE; i++) {
                hands.add(deck.pop());
            }
        }
    
        public boolean canDrawMore() {
            return drawStrategy.canDraw(hands.getPoint());
        }
    
        public void receive(Card card) {
            hands.add(card);
        }
    
        public boolean isNotBlackJack() {
            return hands.isNotInitialSize() || hands.getPoint() != BLACKJACK_SCORE;
        }
    
        public int getScoreMinusBy(User compared) {
            return hands.getPoint() - compared.hands.getPoint();
        }
    
        public Cards openAllCards() {
            return hands;
        }
    
        public abstract Cards openInitialCards();
    
        @Override
        public abstract String toString();
    }
    ユーザーインタフェース.
    どんな方法があるか簡単に見ればいいです.
    印象的なのは保護されたアクセス制御者で、私は実際に初めて接触しました.
  • は保護されています.同じパッケージ内で、他のパッケージのサブクラスから
  • にアクセスできます.
    Dealer.class
    public class Dealer extends User{
        private static final int FIRST_CARD_INDEX = 0;
    
        public Dealer() {
            drawStrategy = new DealerDrawStrategy();
        }
    
        @Override
        public Cards openInitialCards() {
            return new Cards(Collections.singletonList(hands.toList().get(FIRST_CARD_INDEX)));
        }
    
        @Override
        public String toString() {
            return "딜러";
        }
    }
    ユーザーのDelerを継承します.
    非常に簡潔ですが、デラーの責任は要求事項のカードとtoStringを公開することです.
    Player.class
    public class Player extends User{
    
        private final String name;
    
        public Player(String name) {
            if (name.isEmpty()) {
                throw new IllegalArgumentException("이름이 빈 문자열입니다.");
            }
            this.name = name;
            drawStrategy = new PlayerDrawStrategy();
        }
    
        public Player(String name, List<Card> cards) {
            this(name);
            hands = new Cards(cards);
        }
    
        @Override
        public Cards openInitialCards() {
            return hands;
        }
    
        @Override
        public String toString() {
            return name;
        }
    }
    Dealerとあまり違いはありません.
    Users.class
    public class Users implements Iterable<User> {
        private static final int MAX_PLAYERS_COUNT = 8;
        private static final int MIN_PLAYERS_COUNT = 2;
        private static final String SPLIT_DELIMITER = ",";
    
        private final List<User> users;
    
        private Users(List<User> players, User dealer) {
            players.add(dealer);
            if (players.size() > MAX_PLAYERS_COUNT) {
                throw new IllegalArgumentException("블랙잭의 최대 인원은 8명입니다.");
            }
            if (players.size() < MIN_PLAYERS_COUNT) {
                throw new IllegalArgumentException("블랙잭의 최소 인원은 2명입니다.");
            }
            this.users = players;
        }
    
        public static Users of(List<User> players, User dealer) {
            return new Users(players, dealer);
        }
    
        public static Users of(String playerNames, User dealer) {
            return of(Arrays.stream(playerNames.split(SPLIT_DELIMITER))
                    .map(String::trim)
                    .map(Player::new)
                    .collect(Collectors.toList()), dealer);
        }
    
        public List<Player> getPlayers() {
            return users.stream()
                    .filter(user -> user.getClass() == Player.class)
                    .map(user -> (Player) user)
                    .collect(Collectors.toList());
        }
    
        @Override
        public Iterator<User> iterator() {
            return users.iterator();
        }
    }
    継承されたユーザーのオブジェクトを管理するレベル1の集合オブジェクト.
    特に、Iterable<User>を実現しています
    Iterableインタフェースは最上位インタフェースであり、Collectionインタフェースから継承されます.
    内部的にはdepolメソッドforEachメソッドとspliteratorメソッドがあり,抽象メソッドiteratorメソッドを用いる.
    Spliteratorインタフェースは既存の反復器Ivalterrと似ている.
    これはjava 8に追加された並列操作専用のインタフェースです.
    IterableでforEach文を使用することは、ストリームの作成コストを必要とせずに重複文を使用できることを意味します.
    Iterableを実装するクラスでは、
    iterator()メソッドを上書きする必要があります.
    CollectionインタフェースはIterableを継承するため、Collectionインタフェースを継承するインタフェースもIterableを継承し、List、Queue、Setインタフェースを実現するArrayList、LinkedListクラスなどはIterableのItermalメソッドを上書きする.
    そのため、Collectionを書きながらfor-each-loopを使用することができます.
    Iterableが実装されていないオブジェクトについて、Iterator()メソッドを使用してfor eachループを繰り返し実行する方法
    結論として、コンパイラはfor-eachループを通常のfor文に翻訳する.
    奇形コネクタとIterableコネクタ