https://github.com/jiwon3346/videostore
이번 예제는 switch문을 개별 클래스로 만들어 제거한다.
class Moive{
double determineAmount(int daysRented) {
double rentalAmount = 0;
switch (pricecode) {
case REGULAR:
rentalAmount += 2;
if (daysRented > 2)
rentalAmount += (daysRented - 2) * 1.5;
break;
case NEW_RELEASE:
rentalAmount += daysRented * 3;
break;
case CHILDRENS:
rentalAmount += 1.5;
if (daysRented > 3)
rentalAmount += (daysRented - 3) * 1.5;
break;
}
return rentalAmount;
}
}
우리가 제거해야할 switch문이다.
일단 테스트 코드부터 살펴보자
1. 테스트를 polymorphism(다형성)하게 바꾼다.
@Before
public void setUp() {
statement = new Statement("Customer");
newReleaseMovie1 = new Movie("New Release 1", Movie.NEW_RELEASE);
newReleaseMovie2 = new Movie("New Release 2", Movie.NEW_RELEASE);
childrensMovie = new Movie("Childrens", Movie.CHILDRENS);
regular1 = new Movie("Regular 1", Movie.REGULAR);
regular2 = new Movie("Regular 2", Movie.REGULAR);
regular3 = new Movie("Regular 3", Movie.REGULAR);
}
기존의 코드는 어떤 경우에서도 Movie 인스턴스를 생성하고 스위치문을 통해 결과를 반환하는 식이다.
@Before
public void setUp() {
statement = new Statement("Customer");
newReleaseMovie1 = new NewReleaseMovie("New Release 1", Movie.NEW_RELEASE);
newReleaseMovie2 = new NewReleaseMovie("New Release 2", Movie.NEW_RELEASE);
childrensMovie = new ChildrensMovie("Childrens", Movie.CHILDRENS);
regular1 = new RegularMoive("Regular 1", Movie.REGULAR);
regular2 = new RegularMoive("Regular 2", Movie.REGULAR);
regular3 = new RegularMoive("Regular 3", Movie.REGULAR);
}
해당 코드를 각각의 객체로 분리했다.
각각의 클래스는 다음과 같은 형식을 지닌다. (객체 생성시에 HotFix(Alt +Enter)를 사용하면 쉽게 생성 가능)
public class ChildrensMovie extends Movie {
public ChildrensMovie(String title, int priceCode) {
super(title, priceCode);
}
}
여기서 Movie.New_RELEASE와 같은 priceCode 파라미터들은 사실상 중복된다.( 각각의 하위 객체의 파라미터로 받을 필요 없이 그냥 부모 객체에서 가져오면 된다.)
public class Movie {
public static final int CHILDRENS = 2;
public static final int NEW_RELEASE = 1;
public static final int REGULAR = 0;
private String title;
private int pricecode;
public Movie(String title, int priceCode) {
this.title = title;
pricecode = priceCode;
}
public class ChildrensMovie extends Movie {
public ChildrensMovie(String title) {
super(title, Movie.CHILDRENS);
}
}
test{
@Before
public void setUp() {
statement = new Statement("Customer");
newReleaseMovie1 = new NewReleaseMovie("New Release 1");
newReleaseMovie2 = new NewReleaseMovie("New Release 2");
childrensMovie = new ChildrensMovie("Childrens");
regular1 = new RegularMoive("Regular 1");
regular2 = new RegularMoive("Regular 2");
regular3 = new RegularMoive("Regular 3");
}
}
신기한 부분은 super()가 정말 부모 생성자를 함수처럼 가져와 쓴다 ( 완전 하위 클래스와 별개로 작동하는 모습!)
2. push Members Down
이제 switch문은 세개의 클래스로 각각의 케이스가 독립되었으니 한번 발라내어보자.
IntelliJ의 push Members Down을 이용하면 쉽게 리팩토링할 수 있다.
다음과 같이 분리 된다.
public abstract class Movie {
abstract double determineAmount(int daysRented);
abstract int determineFrequentRentalPoint(int daysRented);
}
public class ChildrensMovie extends Movie {
public ChildrensMovie(String title) {
super(title, Movie.CHILDRENS);
}
@Override
double determineAmount(int daysRented) {
double rentalAmount = 0;
switch (getPriceCode()) {
case REGULAR:
rentalAmount += 2;
if (daysRented > 2)
rentalAmount += (daysRented - 2) * 1.5;
break;
case NEW_RELEASE:
rentalAmount += daysRented * 3;
break;
case CHILDRENS:
rentalAmount += 1.5;
if (daysRented > 3)
rentalAmount += (daysRented - 3) * 1.5;
break;
}
return rentalAmount;
}
@Override
int determineFrequentRentalPoint(int daysRented) {
boolean bonusIsEarned = (getPriceCode() == NEW_RELEASE && daysRented > 1);
if (bonusIsEarned)
return 2;
return 1;
}
}
이후에 run with coverage 를 이용하여 실제 사용되는 코드와 사용되지 않는 코드를 눈으로 보고 지워보면 다음과 같다.
public class ChildrensMovie extends Movie {
public ChildrensMovie(String title) {
super(title, Movie.CHILDRENS);
}
@Override
double determineAmount(int daysRented) {
double rentalAmount = 0;
rentalAmount += 1.5;
if (daysRented > 3)
rentalAmount += (daysRented - 3) * 1.5;
return rentalAmount;
}
@Override
int determineFrequentRentalPoint(int daysRented) {
boolean bonusIsEarned = (getPriceCode() == NEW_RELEASE && daysRented > 1);
if (bonusIsEarned)
return 2;
return 1;
}
}
determineFrequentRentalPoint 메소드 역시 NEW_REALSE의 조건을 따진다.
클래스가 이미 분리되었기 떄문에 조건을 따질 필요가 사실 없어진다.
'TDD, CleanCode' 카테고리의 다른 글
클린코더스 강의 리뷰 4. Forms (0) | 2019.05.11 |
---|---|
클린코더스 강의 리뷰 3. Function Structure(2) (0) | 2019.05.10 |
클린코더스 강의 리뷰 2. Function part2 (0) | 2019.05.07 |
[TDD]테스트 주도 개발 예제(Money) - KentBeck (0) | 2019.05.06 |
클린코더스 강의 리뷰 2. Function (0) | 2019.04.22 |