1. Suit
1 2 3 4 5 6 7 | package poker; public enum Suit { SPADE, CLUB, DIA, HEART } | cs |
네종류의 카드를 enum으로 나타냄
카드 종류는 새로 늘거나 줄지 않는 규칙이므로 enum으로 박아넣어도 나중에 건드릴 일이 없음.
2. Card
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package poker; public class Card { final Suit suit; final String label; final int weight; Card( Suit suit, String label, int weight) { this.suit = suit; this.label = label; this.weight = weight; } public Suit getSuit() { return suit; } public String getLabel() { return label; } public int getWeight() { return weight; } @Override public String toString() { return String.format("%s %s(%d)", suit, label, weight); } } | cs |
카드는 suite와 레이블(A, 2, 3, ... 10, J, Q, K), 그리고 가중치값의 조합으로 표현됨.
생성자 visibility를 package private으로 함(함부로 인스턴스를 만들지 못하게 막겠다는 뜻)
필드는 외부에서 바꾸지 못하게 final로 박아버림
getter만 추가
3. Deck
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package poker; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; public class Deck { private List<Card> cards = new ArrayList<>(); public Deck() { reset(); } void reset() { String [] labels = "A:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9 10:10 J:11 Q:12 K:13".split(" "); cards.clear(); for ( Suit suit : Suit.values() ) { for ( String lbl : labels ) { String [] token = lbl.split(":"); // [label, weight] Card c = new Card(suit, token[0], parseInt(token[1])); cards.add(c); } } } private int parseInt(String s) { return Integer.parseInt(s);} public List<Card> cards() { return new ArrayList<>(cards); } public List<Card> findBy(Predicate<Card> f) { return cards.stream() .filter(c -> f.test(c)) .collect(Collectors.toList()); } } | cs |
4. Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package poker; import static org.junit.Assert.*; import java.util.List; import org.junit.Test; public class DeckTest { @Test public void testInit() { Deck deck = new Deck(); List<Card> cards = deck.cards(); assertEquals(52, cards.size()); assertEquals( 13, deck.findBy( c -> c.getSuit() == Suit.SPADE ).size()); assertEquals( 13, deck.findBy( c -> c.getSuit() == Suit.CLUB ).size()); assertEquals( 13, deck.findBy( c -> c.getSuit() == Suit.HEART ).size()); assertEquals( 13, deck.findBy( c -> c.getSuit() == Suit.DIA ).size()); } } | cs |
만들었으니까 테스트.
Line16 : 52장이 맞는지 확인
Line;17 ~ 20 : 각각의 suite별로 13장이 맞는지 확인
원래는 각각의 13장이 A부터 K까지 13개씩 있는지도 확인해야 하지만 오류가 없을거라는 확신이 들기때문에 생략함.
(모든 경우를 다 테스트하지 않음. 확신이 없는 부분만 테스트함)
5. 없는 것
pick() 구현 안됨.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package poker; public class Deck { private List<Card> cards = new ArrayList<>(); public Deck() { reset(); } void reset() { // .... } private int parseInt(String s) { return Integer.parseInt(s);} public Card pick() { return null; } } | cs |
52장의 카드를 하나씩 빼서 게임 참여자들에게 건네는 행위를 모델링할때 필요한 구현.
실제 게임에서는 딜러가 카드가 뒤섞인 데크에서 하나씩 빼겠지만(랜덤모드) 사기도박 사이트같은 경우 플레이어의 패를 모니터링하면서 이기지 못하는 카드를 하나씩 건넨단말야?(사기모드)
즉, pick 내부 구현이 그때그때 달라진다는 뜻이지.
그래서 다음과 같은 인터페이스 도입을 함 검토해볼 필요가 있지.
1 2 3 4 5 6 7 8 9 | package poker; import java.util.List; public interface ICardPicker { public Card pick ( List<Card> givens ) ; } | cs |
주어진 배열 안의 카드 중에서 하나만 선택해서 반환하는게 ICardPicker가 하는 일
얘는 카드만 선택해야함. 그러니까 지가 리스트에서 카드를 없앤다거나, 이런 짓을 못하게 해야함.
이렇게 되면 Deck는 외부에서 ICardPicker 구현체를 전달받는 생성자가 추가로 필요해짐.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package poker; public class Deck { private List<Card> cards = new ArrayList<>(); private ICardPicker picker public Deck() { this( new RandomPicker() ); } public Deck(ICardPicker picker ) { this.picker = picker; } void reset() { // .... } private int parseInt(String s) { return Integer.parseInt(s);} public Card pick() { return null; } } | cs |
그러면 pick 메소드 구현은 아래와 같은 정도가 되지 않을까?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package poker; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; public class Deck { private List<Card> cards = new ArrayList<>(); private ICardPicker picker; public Deck() { this( new RandomPicker()); } public Deck(ICardPicker picker) { this.picker = picker; reset(); } public Card pick() { Card chosen = picker.pick( Collections.unmodifiableList(cards)); cards.remove(chosen); return chosen; } } | cs |
Collections.unmodifiable(..) 은 picker가 리스트를 건드려서 고치는 행위를 막기 위해서 한번 더 감싸줌.
물론 ICardPicker쪽 문서에는 argument로 넘어온 card 배열이 읽기 전용이라고 써놓아야겠지?
아니면 parameter이름을 readOnly라고 해서 뭔지 알아먹게 하든가...
그럼 이제 RandomPicker 구현체만 만들면 됨.
이건 각자 관심있는 사람이...
와.... 지렷다 진짜 감사합니다