ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 숫자야구게임 구현(1)
    공부/자바 플레이그라운드 with TDD, 클린코드 2022. 2. 13. 20:25

    TDD가 익숙치 않아 프로덕션 코드를 구현하고 단위 테스트를 적용시켜 버렸다..

    숫자야구게임 피드백을 보고 다시 구현할 것이다.


     

    BaseBallGameTest

    public class BaseBallGameTest {
        @ParameterizedTest
        @CsvSource({
                "845,낫싱",
                "891,1볼",
                "137,포볼",
                "123,1볼 1스트라이크",
                "723,2스트라이크",
                "713,3개의 숫자를 모두 맞히셨습니다! 게임 종료"
        })
        void baseBallGameCoreTest(String input, String expected) throws Exception {
            BaseBallGameCore bbgc = new BaseBallGameCore("713");
            bbgc.calc(input);
    
            assertThat(bbgc.getStatus()).contains(expected);
        }
    
        @Test
        void baseBallGameCoreExceptionTest() throws Exception {
            Throwable thr = catchThrowable(() -> new BaseBallGameCore("71"));
    
            assertThat(thr).hasMessageContaining("적어도 3자리 이상의 길이를 입력해 주십시오.");
        }
    
        @Test
        void baseBallGameCoreStrikeExceptionTest() throws Exception {
            BaseBallGameCore bbgc = new BaseBallGameCore("713");
            Throwable thr = catchThrowable(() -> bbgc.strike("12"));
    
            assertThat(thr).hasMessageContaining("입력한 값의 길이와 정답의 길이가 다릅니다.");
        }
    }

     

    BaseBallGameCore

    public class BaseBallGameCore {
    
        private String answer;
        protected int ball = 0;
        protected int strike = 0;
    
        public BaseBallGameCore(String answer) throws Exception {
            if (!validateLength(answer))
                throw new Exception("적어도 3자리 이상의 길이를 입력해 주십시오.");
    
            this.answer = answer;
        }
    
        private boolean validateLength(String answer) {
            return answer != null && answer.length() >= 3;
        }
    
        public boolean strike(String input) throws Exception {
            if (answer.length() != input.length())
                throw new Exception("입력한 값의 길이와 정답의 길이가 다릅니다.");
    
            resetStatus();
    
            if (answer.equals(input)) {
                strike = answer.length();
                return true;
            }
    
            calc(input);
    
            return false;
        }
    
        private void resetStatus() {
            ball = 0;
            strike = 0;
        }
    
        public void calc(String input) {        
            for(int i=0; i<input.length(); i++) {
                calcStrikeAndBall(input.charAt(i), i);
            }
        }
    
        private void calcStrikeAndBall(char input, int index) {
            if(calcStrike(input, index)) {
                strike++;
                return;
            }
    
            if(calcBall(input, index)) {
                ball++;
                return;
            }
        }
    
        private boolean calcStrike(char input, int index) {
            return equals(input, answer.charAt(index));
        }
    
        private boolean equals(char c1, int c2) {
            return c1 == c2;
        }
    
        private boolean calcBall(char input, int index) {
            return Stream.of(remains(index).split(""))
                    .map(s -> s.charAt(0))
                    .filter(c -> c == input)
                    .count() > 0;
        }
    
        private String remains(int index) {
            return answer.substring(0, index) + answer.substring(index + 1);
        }
    
        public String getStatus() {
            StringBuilder sb = new StringBuilder();
    
            if(ball == answer.length())
                return "포볼";
    
            if(strike == answer.length())
                return String.format("%d개의 숫자를 모두 맞히셨습니다! 게임 종료", strike);
    
            if(ball > 0)
                sb.append(ball + "볼 ");
    
            if(strike > 0)
                sb.append(strike + "스트라이크");
    
            if(sb.length() <= 0)
                return "낫싱";
    
            return sb.toString();
        }
    }

    영상 시청 후

    더보기
    1. 요구사항 분석으로 TodoList를 작성
    2. 필요한 기능 을 상세히
    3. 기능 단위로 *Test.java 작성!!!!!!!!
    4. 테스트를 하기 쉬운 코드를 먼저 (UI, DB, 랜덤은 어려움)
    5. 테스트 코드 먼저 실패하도록 작성
    6. 컴파일 에러 해결하기위해 테스트 작성 -> 코드 생성 역순으로 하는것에 익숙해지자
    7. 테스트 코드를 성공하도록 프로덕션 코드 작성
    8. 리팩토링
    9. IntelliJ 의 단축키 완전 대박
      - 오른쪽 클릭 Reactor 에서 단축키 따로 외우자 굿~
    10. 하드코딩 하지말고 상수값 사용
    11. commit 단위는 하나의 테스트코드 + 프로덕션코드를 뭉탱이로 올리고, 테스트 명칭을 feature 로 commit 하면 될듯
    12. 큰 덩어리는 작은 덩어리로 쪼개서 테스트 가능하도록 연습
    13. 일단 클래스 메서드, static으로 기능작성 TDD 편하게 (상태를 가지면 복잡하니)
    14. 클래스 메서드 작성할 때 같은 클래스의 다른 인스턴스에 직접적으로 멤버변수에 접근하지말고 객체에 메세지를 전달한다는 생각으로 이또한 메서드로 빼자
    15. Enum 내부 메서드를 활용해 Enum에도 메세지를 전달하는 메서드를 이용하자
    16. 원시값, 문자열를 감싸서 유효성 검사를 하는등 클래스분리도 해보자
    17. 같은 클래스의 값을 검사할 때 if문을 제거하기위해 인터페이스로 메서드를 override하는 방법도 좋다

    직접 구현하고 다시 게시글 작성하자

    댓글

Designed by Tistory.