[Refactoring] 냄새 12. 반복되는 switch 문
07 Jan 2023해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.
냄새 12. 반복되는 switch 문
- 예전에는 switch 문이 한번만 등장해도 코드 냄새로 생각하고 다형성 적용을 권장했다.
- 하지만 최근에는 다형성이 꽤 널리 사용되고 있으며, 여러 프로그래밍 언어에서 보다 세련된 형태의 switch 문을 지원하고 있다.
- 따라서 오늘날은 “반복해서 등장하는 동일한 switch 문”을 냄새로 여기고 있다.
- 반복해서 동일한 switch 문이 존재할 경우, 새로운 조건을 추가하거나 기존의 조건을 변경할 때모든 switch 문을 찾아서 코드를 고쳐야 할지도 모른다.
예제 코드
아래의 코드는 Full-time, Part-time, Temporal의 각각에 타입을 switch 문을 통해 휴게시간을 반환하는 로직이다.
아래의 코드를 Factory 패턴을 이용하여, 리팩토링 해보자.
- Employee
public class Employee {
public int vacationHours(String type) {
int result;
switch (type) {
case "full-time": result = 120;
case "part-time": result = 80;
case "temporal": result = 32;
default: result = 0;
}
return result;
}
}
먼저 당연하게도 테스트를 돌려보도록 하자.
테스트를 실패하는 것을 볼 수 있다. 여기서 테스트의 중요성을 확인 해볼 수 있다.
나중에 리팩토링 후에 로직이 제대로 돌아가지 않을경우 리팩토링이 문제였는지, 원래의 코드가 문제였는지 파악을 하기 어렵기 때문이다.
해당 버그는 간단한다.
switch문에 break;로 빠져나오지 않기 때문에, default의 조건까지 계속 타고 내려가기 때문에 0을 return 한다.
해당 코드를 수정하고 다시 테스트를 돌려보자. 테스트를 성공하는 것을 확인할 수 있다.
이제 리팩토링을 진행해 보도록 하자.
먼저 Employee를 추상화 하여, 자식객체에서 구현할 수 있도록 한다.
public abstract class Employee {
public abstract int vacationHours();
}
각 타입에 맞춰 Emplyee를 상속하는 자식 객체를 만들어 준다.
그리고 vacationHours
를 @Override받아 구현해주도록 하자.
public class FullTimeEmployee extends Employee {
@Override
public int vacationHours() {
return 120;
}
}
public class PartTimeEmployee extends Employee {
@Override
public int vacationHours() {
return 80;
}
}
public class TemporalEmployee extends Employee {
@Override
public int vacationHours() {
return 32;
}
}
switch 문 대신 하위 instance문을 반환하는 Factory 클래스를 만들어 준다.
type을 key값으로 가지고 자식객체를 값으로 가지는 HashMap을 만들어 type이 들어올경우 알맞은 자식객체의 인스턴스를 생성하도록 한다.
public class EmployeeFactory {
private static Map<String, Employee> employeeMap = new HashMap<>();
static {
employeeMap.put("full-time", new FullTimeEmployee());
employeeMap.put("part-time", new PartTimeEmployee());
employeeMap.put("temporal", new TemporalEmployee());
}
public static Employee createEmployee(String type) {
if (employeeMap.containsKey(type))
return getInstanceEmployee(type);
else
throw new IllegalArgumentException(type);
}
private static Employee getInstanceEmployee(String type) {
return employeeMap.get(type);
}
}
이제 테스트 코드를 수정하고 다시 테스트를 돌려보도록 하자.