1. 가장 쉬운 예제를 찾는다.
1-1 1로 소인수 분해를 했을 때의 얻는 값.
public class PrimeFactorsTest {
@Test public void canFactorIntoPrimes() {
assertEquals(Arrays.asList(), of(1));
}
private List<Integer> of(int n) {
return new ArrayList<Integer>() ;
}
}
assertEquals는 기대한 값이 앞에 , 실제 값이 뒤에 온다 assertEquals(예상값, 실제값)
of 메소드는 테스트를 위해 만들었고,
n의 소인수를 리스트로 반환한다.
1-2 헌데 예상값의 List는 Object를 실제값은 Integer를 반환한다.
public class PrimeFactorsTest {
@Test public void canFactorIntoPrimes() {
assertEquals(list(), of(1));
}
private List<Integer> list() {
return Arrays.asList();
}
private List<Integer> of(int n) {
return new ArrayList<Integer>() ;
}
}
이 문제를 해결하기 위해서 extract method를 이용하여 리턴 타입을 설정할 수 있게 된다.
2. 다음 쉬운 예제 찾기
2-1 2의 소인수 찾기
@Test public void canFactorIntoPrimes() {
assertEquals(list(), of(1));
assertEquals(list(2),of(2));
}
다음과 같이 테스트를 설정할 수 있지만 list는 파라미터를 가지지 않는다.
private List<Integer> list(Integer... ints) {
return Arrays.asList(ints);
}
다음과 같이 argument를 동적으로 받을 수 있고, 그 값이 배열로 저장되게 하여 리스트를 생성한다.
private List<Integer> of(int n) {
ArrayList<Integer> factors = new ArrayList<Integer>();
if(n==2)
factors.add(2);
return factors;
}
of(2)일 경우 그린라이트를 만들기 위해 다음과 같이 만들었다.
기존의 new ArrayList를 variable로 만들고
n==2인 경우에는 2를 추가하고 아닌 경우에는 여전히 new ArrayList의 초기화값인 빈 배열을 반환한다.
이때 if(n==2)는 아주 특별한 경우이다.
이 경우를 점점 generic(일반화) 시켜야 하고 다음과 같이 수정해볼 수 도 있다.
if(n > 1)
factors.add(2);
여전히 특별하지만 이전 경우보단 더 포괄적으로 작동 되어 진다.
3. 이와 같은 식으로 소수의 소인수는 다음과 같이 포괄적으로 코드를 짤 수 있다.
@Test public void canFactorIntoPrimes() {
assertEquals(list(), of(1));
assertEquals(list(2),of(2));
assertEquals(list(3),of(3));
}
private List<Integer> list(Integer... ints) {
return Arrays.asList(ints);
}
private List<Integer> of(int n) {
ArrayList<Integer> factors = new ArrayList<Integer>();
if(n > 1)
factors.add(n);
return factors;
}
4. 테스트 케이스의 중복을 제거 하자
@Test public void canFactorIntoPrimes() {
assertEquals(list(), of(1));
assertEquals(list(2),of(2));
assertEquals(list(3),of(3));
}
다음 테스트 코드는 구조가 같다.
다른 부분을 먼저 variable로 바꾸자.
@Test public void canFactorIntoPrimes() {
List<Integer> list = list();
int n = 1;
assertEquals(list, of(n));
assertEquals(list(2),of(2));
assertEquals(list(3),of(3));
}
그리고 extract method를 한다.
@Test public void canFactorIntoPrimes() {
List<Integer> list = list();
int n = 1;
assertPrimeFactors(list, n);
assertPrimeFactors(list(2), 2);
assertPrimeFactors(list(3), 3);
}
그리고 extract하기 위한 변수들을 다시 inline 시킨다.
public class PrimeFactorsTest {
@Test public void canFactorIntoPrimes() {
assertPrimeFactors(list(), 1);
assertPrimeFactors(list(2), 2);
assertPrimeFactors(list(3), 3);
}
파라미터 값이 예측값, 실제값이다.
실제값, 예측값으로 바꾼다면 훨씬 가독성이 좋아 질 것이다.
change signiture를 이용하여 파라미터의 위치를 바꾼다.
@Test public void canFactorIntoPrimes() {
assertPrimeFactors(1, list());
assertPrimeFactors(2, list(2));
assertPrimeFactors(3, list(3));
}
5. 소수가 아닌 수를 나눈 소인수 값들
assertPrimeFactors(4,list(2,2));
4의 소인수는 2, 2이다. (엄밀하게 따지면 중복을 허용하지는 않지만...)
해당 문제를 해결할 of를 추가하자.
private List<Integer> of(int n) {
ArrayList<Integer> factors = new ArrayList<Integer>();
if(n > 1) {
if (n % 2 == 0) {
factors.add(2);
n /= 2;
}
if(n >1)
factors.add(n);
}
return factors;
}
2로 나누어 떨어지면 2로 나누고, 그 수가 1보다 크다면(2일 경우를 방지) 해당 n을 다시 추가한다.
5-2 2개 이상이라면?
assertPrimeFactors(8,list(2,2,2));
해당 케이스는 아마 결과값이 list(2,2)일 것이다.
이를 수정하기 위해
if (n > 1) {
while (n % 2 == 0) {
factors.add(2);
n /= 2;
}
}
if (n > 1)
factors.add(n);
return factors;
가운데 if절을 while로 바꾸어 주었다.
5-3 3인 소인수들은?
assertPrimeFactors(9,list(3,3));
위의 경우에 3에 처리하는 로직이 없기 때문에 다음과 같이 복사한 후 수정한다.
while (n % 2 == 0) {
factors.add(2);
n /= 2;
}
while (n % 3 == 0) {
factors.add(3);
n /= 3;
}
5-4 중복을 제거하자
int divisor = 2;
while (n > 1) {
while (n % divisor == 0) {
factors.add(divisor);
n /= divisor;
}
divisor++;
}
n 이 1이 될때까지 계속 divisor를 나누면서 divisor를 더해준다.
이 때 안쪽의 while문을 for문으로 바꿀 수 있는데
for (;n % divisor == 0;) {
factors.add(divisor);
n /= divisor;
}
정지 조건만 저렇게 만들고 초기조건과 증가 조건은 내버려 둔다.
for (;n % divisor == 0;n /= divisor) {
factors.add(divisor);
}
증가 조건도 있으니 올린다.
바깥 조건도 동일하게 바꾼다.
for (;n > 1;divisor++) {
for (;n % divisor == 0;n /= divisor)
factors.add(divisor);
}
6. 복잡한 케이스를 추가해보자.
assertPrimeFactors(2*2*2*3*3*5*7*11*13,list(2,2,2,3,3,5,7,11,13));
코드가 잘 작성 됬다면 정상적으로 동작할 것이다.
'TDD, CleanCode' 카테고리의 다른 글
React 테스트 (Jest, Enzyme) (0) | 2019.08.10 |
---|---|
클린코더스 강의 리뷰 5. TDD 예제 (wordWrap) (0) | 2019.05.21 |
클린코더스 강의 리뷰 5. TDD 예제 (Bowling Game) (0) | 2019.05.20 |
클린코더스 강의 리뷰 5. TDD의 기본 예시 (0) | 2019.05.13 |
클린코더스 강의 리뷰 4. Forms (0) | 2019.05.11 |