Codewars 문제풀기 (04/10)
Playing with digits
- int 두개를 인수로 받는다.
- 첫번째 인수 n을 digit단위로 쪼개고 각 digit를 두번째 인수 p부터 1씩 증가한값을 제곱하여 더한다.
- 계산한 값을 첫번째 인수 n으로 나누었을대 몫을 반환한다.
- 나머지가 있다면 -1을 반환한다.
- digPow(89, 1) –> 8¹ + 9² = 89 * 1 –> 1
- digPow(46288, 3) –> 4³ + 6⁴+ 2⁵ + 8⁶ + 8⁷ = 2360688 = 46288 * 51–> 51
- digPow(92, 1) –> 9¹ + 2² = 13 = 13 * k != 92 –> -1
1. Test와 리팩토링
-
인수로 89, 1이 입력되면 1을 리턴해야한다.
-
테스트 코드
@Test public void testShouldReturn1WhenNumberIs89AndPowIs1() { // Given: Set number 89 and pow 1 int givenNumber = 89; int givenPow = 1; // When: Call digPow method long actual = DigPow.digPow(givenNumber, givenPow); // Then: Should Return 1 --> 8^1 + 9^2 = 89 --> 89 = 89 * 1 --> 1 assertEquals(1, actual); }
-
실제 코드
public class DigPow { public static long digPow(int n, int p) { String toString = String.valueOf(n); String[] digits = toString.split(""); long calResult = 0; for (int i = 0; i<digits.length; i++) { calResult += Math.pow(Integer.parseInt(digit), p); p++; } return calResult % n == 0 ? calResult / n : -1; }
- n을 10으로 나눈 나머지값을 배열에 저장하는 방법도 있는데 코드가 길어질것 같아서 그냥 String으로 변환 후 split메서드로 쪼개서 배열에 저장했다. 계산할때는 parseInt 메서드를 사용해서 다시 int로 바꾸어 계산했다.
-
-
다른 테스트에도 다 먹히는것 같아서 테스트 해보니 다 통과했다.
-
리팩토링
-
계산하는 부분을 메서드로 따로 추출했다. toString이라는 지역변수도 삭제했다.
public class DigPow { public static long digPow(int n, int p) { String[] digits = String.valueOf(n).split(""); long calResult = calculateWithDigitsAndPow(digits, p); return calResult % n == 0 ? calResult / n : -1; } private static long calculateWithDigitsAndPow(String[] digits, int p) { long calResult = 0; for (String digit : digits) { calResult += Math.pow(Integer.parseInt(digit), p); p++; } return calResult; } }
-
-
이렇게 하고 보니까 테스트를 잘못 만든것 같다. 테스트를 그냥 예제에 맞게 테스트만 했는데.. 그래서 생각을 해봤다.
-
옛날에 처음 TDD를 강의로 배울때 계산기 기능을 TDD를 이용해서 구현하는 실습을 했었다. 그떄 생각해보면 기능(메서드)별로(+, -, *, /) 테스트를 만들었다. 테스트 메서드명도 +연산을해야한다. -연산을해야한다.. 등으로 만들었다.
-
그 기능을 고려해서 테스트 코드를 다시 짜보았다.
-
먼저 첫번째 인수를 digit단위로 쪼개는 메서드 기능을 구현해야한다.
-
테스트 코드
@Test public void testShouldSplitFirstParameterByDigit() { // Given: Set first parameter int given = 89; // When: Call split method String[] actual = DigPow.split(given); // Then: Should return {8, 9} assertArrayEquals(new int[]{8,9}, actual); }
-
실제 코드
public class DigPow { public static String[] split(int n) { return String.valueOf(n).split(""); } }
-
-
digit단위로 쪼갠 값과 두번째 인수 pow를 이용해서 문제에서 요구하는 계산을 해야한다.
-
테스트 코드
@Test public void testShouldCalculateWithDigitAndPow() { // Given: Set digit array and pow int givenNumber = 89; int givenPow = 1; String[] givenArray = DigPow.split(givenNumber); // When: Call calculate method long actual = DigPow.calculate(givenArray, givenPow); // Then: Should return 89 assertEquals(89, actual); }
-
실제 코드
public class DigPow { public static String[] split(int n) { return String.valueOf(n).split(""); } public static long calculate(String[] digits, int pow) { long result = 0; for (String digit : digits) { result += Math.pow(Integer.parseInt(digit), pow); pow++; } return result; } }
-
-
calculate를 통해 얻은 값을 첫번째 인수 n으로 나눴을 때 나머지가 없다면 몫을 리턴하고 나머지가 있다면 -1을 리턴한다.
-
테스트 코드
@Test public void testShouldReturnQuotientWhenDivisionCalculateResultFirstParameterIsNoReminder() { // Given: Set number and pow int givenNumber = 89; int givenPow = 1; // When: Call calculate method long actual = DigPow.digPow(89, 1); // Then: Should return 1 --> 8^1 + 9^2 = 89 --> 89 = 89*1 --> 1 assertEquals(1, actual); }
-
실제 코드
public class DigPow { public static long digPow(int n, int p) { String[] digits = split(n); long calResult = calculateWithDigitsAndPow(digits, p); return calResult % n == 0 ? calResult / n : -1; } public static String[] split(int n) { return String.valueOf(n).split(""); } public static long calculate(String[] digits, int pow) { long result = 0; for (String digit : digits) { result += Math.pow(Integer.parseInt(digit), pow); pow++; } return result; } }
-
-
리팩토링
-
split메서드는 삭제해도 될 것 같다.
package playingWithDigits_20200410; public class DigPow { public static long digPow(int n, int p) { String[] digits = String.valueOf(n).split(""); long calResult = calculate(digits, p); return calResult % n == 0 ? calResult / n : -1; } public static long calculate(String[] digits, int pow) { long result = 0; for (String digit : digits) { result += Math.pow(Integer.parseInt(digit), pow); pow++; } return result; } }
- split메서드를 삭제하면 테스트코드에서 컴파일 에러가 발생하는데.. 이럴때는 테스트 코드를 삭제해야 하는건지 아니면 테스트코드도 리팩토링해야하는지 잘 모르겠다.
-
2. 답 비교, 느낀점
Best Practice 가장 많이 받은 코드
public class DigPow {
public static long digPow(int n, int p) {
String intString = String.valueOf(n);
long sum = 0;
for (int i = 0; i < intString.length(); ++i, ++p)
sum += Math.pow(Character.getNumericValue(intString.charAt(i)), p);
return (sum % n == 0) ? sum / n : -1;
}
}
- getNumericValue를 사용했다. 문제 푸는 방식은 비슷하다.
- 이번 문제를 풀면서 테스트 코드 작성에 좀 더 생각하는 계기가 됐다. 무작정 문제에서 제공해주는 테스트를 통과시키려고 테스트를 기계적으로 만들었는데 문제에 대한 기본 설계 후 가장 작은것을 만족하는 메서드를 먼저 만들고 점점 살을 붙여나가는 방식으로 테스트코드를 구현해야한다고 느꼈다.
- 근데 내가 한 방식이 맞는지는 잘 모르겠다. 처음 테스트 통과용으로 만든 테스트 메서드들이 뭔가 이상하다고 느껴 문제를 다 풀고나서 다시 푼것이라서 이렇게 적용시켜도 되는건지는 잘 모르겠다. 다음 이와 유사한 문제가나오면 적용해봐야 할것같다.
댓글남기기