반응형

카카오 신입 공채 1차 코딩 테스트에 있는 알고리즘 문제를 풀어봅니다.

다트 게임(난이도: 하) [상편 - 필수 문자열에 해당하는 객체 생성]


상편


“점수|보너스|[옵션]”으로 이루어진 문자열 3세트. 이므로,
"1S","1D","1T"와 같은 "점수|보너스"는 반드시 존재합니다.

점수객체, 보너스객체, 점수객체+보너스객체(Object Compoistion[조합 객체])를 만들어 역할을 담당하게 합니다.

1. 다트 포인트 객체 생성 ("2S","2D","2T") - 앞쪽 숫자

알고리즘 규칙 2. 각 기회마다 얻을 수 있는 점수는 0점에서 10점까지이다.

먼저 테스트 코드를 만들어봅니다. (10점이지만 20점 까지로 해보았습니다.)

package kakao.dart;

import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class BasicPointTest {

    @Test
    public void 포인트_제한_0에서_20() {
        for (int i = 0; i < 20; i++) {
            new PointNumber(String.valueOf(i));
        }
    }

    @Test (expected = IllegalArgumentException.class)
    public void 포인트_최대값_초과_21() {
        new PointNumber("21");
    }
}

해당 조건에 부합하는 객체를 만듭니다.

package kakao.dart;

public class PointNumber {

    private static final Long MIN_NUMBER = 0l;
    private static final Long MAX_NUMBER = 20l;

    private Long number;

    public PointNumber(String numeric) {
        if (numeric == null) throw new IllegalArgumentException();
        Long number = Long.valueOf(numeric);
        if (outOfNumber(number)) throw new IllegalArgumentException();

        this.number = number;
    }

    private boolean outOfNumber(Long number) {
        return number < MIN_NUMBER || MAX_NUMBER < number;
    }

    public long toLong() {
        return number;
    }
}

기본 점수 객체를 만들어 놓으면, 이후에 객체 사용시 validation check(null, invalid value)에 대한 부담을 줄일 수 있습니다.

다트 게임은 0~20까지의 점수를 기준으로 했습니다. (bull (Single bull, Double bull)은 제외했습니다.)

outOfNumber() : 생성시 핵심 로직으로 작동합니다.

2. 다트 보너스 객체 생성 ("2S","2D","2T") - 뒷쪽 문자

알고리즘 규칙 3. 점수와 함께 Single(S), Double(D), Triple(T) 영역이 존재하고 각 영역 당첨 시 점수에서 1제곱, 2제곱, 3제곱 (점수^1 , 점수^2 , 점수^3 )으로 계산된다.

@Test
    public void 보너스_계산() {
        assertThat(2l, is(Bonus.SINGLE.calculate(2)));
        assertThat(4l, is(Bonus.DOUBLE.calculate(2)));
        assertThat(8l, is(Bonus.TRIPLE.calculate(2)));
    }

계산을 했을경우 왼쪽의 값과 같도록 테스트코드를 만들었습니다.

package kakao.dart;

import java.util.Arrays;
import java.util.function.Function;

public enum Bonus {
    SINGLE("S", value -> value),
    DOUBLE("D", value -> (long) Math.pow(value, 2)),
    TRIPLE("T", value -> (long) Math.pow(value, 3));

    private String shortName;
    private Function<Long, Long> expression;

    Bonus(String shortName, Function<Long, Long> expression) {
        this.shortName = shortName;
        this.expression = expression;
    }

    public long calculate(long value) {return expression.apply(value);}

    public static Bonus getByShortName(String shortName) {
        return Arrays.asList(Bonus.values()).stream()
                .filter(bonus -> bonus.shortName.equals(shortName))
                .findFirst()
                .orElseThrow(IllegalArgumentException::new);
    }
}

java8에서 개발했기 때문에 Function<Long, Long>이 가능케 되는데요.
자세한 내용은 우아한 형제의 이동욱님께서 작성한 Java Enum 활용기를 참고하세요~

java 7에서 하는 방법도 나와있답니다 :)

calculate() : 핵심로직으로 제곱 연산을 담당합니다.

3. 조합 객체 만들기

점수|보너스|[옵션]”으로 이루어진 문자열

3세트.알고리즘 규칙 8. Single(S), Double(D), Triple(T)은 점수마다 하나씩 존재한다.
다트 포인트와 보너스는 항상 붙어 다니므로, 1개의 객체로 취급합니다.

    @Test (expected = IllegalArgumentException.class)
    public void 포인트_보너스_부재() {
        new BasicPoint("1");
    }

    @Test
    public void 포인트_계산() {
        assertThat(2L, is(new BasicPoint("2S").calculate()));
        assertThat(4L, is(new BasicPoint("2D").calculate()));
        assertThat(8L, is(new BasicPoint("2T").calculate()));
    }
package kakao.dart;

import java.util.Optional;

public class BasicPoint {

    private static final String POINT_MATCH = "[^\\d]{1,2}";

    private PointNumber pointNumber;
    private Bonus bonus;

    public BasicPoint(String text) {
        if (text == null || text.isEmpty()) throw new IllegalStateException();
        pointNumber = matchPoint(text);
        bonus = matchArea(text);
    }

    private PointNumber matchPoint(String text) {
        return new PointNumber(Optional.of(text.split(POINT_MATCH)).orElseThrow(IllegalArgumentException::new)[0]);
    }

    private Bonus matchArea(String text) {
        return Bonus.getByShortName(text.substring(text.length()-1));
    }

    public Long calculate() {
        return bonus.calculate(pointNumber.toLong());
    }
}

 optional.of()는 단순히 if문을 줄이기 위해 사용하였습니다.

matchPoint, matchArea 매쏘드 에서 의도한 값이 들어오지 않으면 IllegalStateException을 던지도록 하였습니다. 

이렇게 해서 다트 게임의 필수 값을 구현했습니다.

객체를 객체답게 사용하기위해

규칙 1: 한 메서드에 오직 한 단계의 들여쓰기만 한다.
규칙 2: else 예약어를 쓰지 않는다.
규칙 3: 모든 원시값과 문자열을 포장한다.
규칙 6: 모든 엔티티를 작게 유지한다.
규칙 7: 2개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
규칙 8: 일급 콜렉션을 쓴다.
규칙 9: 게터/세터/프로퍼티를 쓰지 않는다.

출처 : 효과적으로 TDD, 리팩토링, OOP를 연습하는 방법


반응형

'프로그래밍 > 알고리즘' 카테고리의 다른 글

캐시(난이도: 하) [하편]  (0) 2018.02.23
캐시(난이도: 하) [상편]  (0) 2018.02.22
다트 게임(난이도: 하) [하편]  (0) 2018.02.14
비밀 지도(난이도: 하) JAVA  (0) 2018.02.14

+ Recent posts