때는 약 1년 전으로 돌아가 우테코 프리코스를 하던 시절..
당시 과제들의 최우선 순위로 해야할 미션은 바로 클린 코딩이었다.
그 중에서도 나에게 신선한 충격을 주었던 건, if문을 최대 2중 중첩까지만 쓸 수 있게 제한한 것이었다.
If문의 중첩 수를 줄이기 위해서는 최대한 메서드를 쪼개거나, 도중에 return 하는 등의 방식을 사용했다.
그리고 인턴 기간 중에도 수많은 if문들은 나를 괴롭혔다.
내가 짠 코드도 그렇게 짜놓으면 보기 힘든데 남이 보면 얼마나 힘들까.
그래서 다음 인턴이 왔을 때 내 코드를 좀 편하게 보기 위해 엄청나게 리펙토링 하고 간 기억이 난다.
이제 오늘 데브코스에서 배운 강의를 활용해 나름의 팁? 을 써보려 한다.
public enum CalculateType {
ADD, MINUS, MULTIPLY, DIVIDE
}
public class CalculateCommand {
private CalculateType calculateType;
private int num1;
private int num2;
// 생성자, getter 생략
}
public int someMethod(CalculateCommand calculateCommand) {
CalculateType calculateType = calculateCommand.getCalculateType();
int num1 = calculateCommand.getNum1();
int num2 = calculateCommand.getNum2();
int result = 0;
if(calculateType != null && calculateType.equals(CalculateType.ADD)) {
result = num1 + num2;
} else if(calculateType != null && calculateType.equals(CalculateType.MINUS)) {
result = num1 - num2;
} else if(calculateType != null && calculateType.equals(CalculateType.MULTIPLY)) {
result = num1 * num2;
} else if(calculateType != null && calculateType.equals(CalculateType.DIVIDE)) {
if(num2 == 0) {
throw new RuntimeException("0으로 나눌 수 없습니다.");
} else {
result = num1 / num2;
}
}
return result;
}
저 수많은 if문을 보아라...!
우선 좀 불편한 지점들을 보면
1. if문들에 calculateType != null 부분이 중복되어 있다.
2. 조건문에 실행되는 함수들이 어느정도 패턴이 유사한데 이를 묶을 수 없을까?
3. num2가 0인 경우를 사전에 미리 예외처리하면 어떨까?
먼저 1번과 3번을 실행해보자!
if(calculateType == null) {
return result;
}
if(calculateType.equals(CalculateType.DIVIDE) && num2 == 0) {
throw new RuntimeException("0으로 나눌 수 없습니다.");
}
if(calculateType.equals(CalculateType.ADD)) {
result = num1 + num2;
} else if(calculateType.equals(CalculateType.MINUS)) {
result = num1 - num2;
} else if(calculateType.equals(CalculateType.MULTIPLY)) {
result = num1 * num2;
} else if(calculateType.equals(CalculateType.DIVIDE)) {
result = num1 / num2;
}
좀 괜찮아보인다. 하지만 단순히 null일 때 result를 return하는 것보다는 예외를 던지는 것이 조금 더 알맞는 것 같고,
처음부터 calculateType이라는 객체가 생성될 때 이를 처리할 수 없을까?
public CalculateCommand(CalculateType calculateType, int num1, int num2) {
if(calculateType == null) {
throw new RuntimeException("CalculateType은 필수 값 입니다.");
}
if(calculateType.equals(CalculateType.DIVIDE) && num2 == 0) {
throw new RuntimeException("0으로 나눌 수 없습니다.");
}
this.calculateType = calculateType;
this.num1 = num1;
this.num2 = num2;
}
생성자 코드에서 넘겨받은 값에 대해 예외처리를 해줌으로써 이제 생성시점에서 바로 알 수 있게 되었다.
그러면 이제 검증 로직을 서비스단에서 해줄 필요가 없어졌다.
또 2번인 패턴을 묶어보자.
해당 Enum 변수에 따라오는 함수를 생성자 시점에서 만들어놓는 것이다.
public enum CalculateType {
ADD ((num1, num2) -> num1 + num2),
MINUS ((num1, num2) -> num1 - num2),
MULTIPLY ((num1, num2) -> num1 * num2),
DIVIDE ((num1, num2) -> num1 / num2);
CalculateType(BiFunction<Integer, Integer, Integer> expression) {
this.expression = expression;
}
private BiFunction<Integer, Integer, Integer> expression;
public int calculate(int num1, int num2) {
return this.expression.apply(num1, num2);
}
}
이렇게 enum의 생성자 시점에서 해당 호출할 함수를 가지게 해놓으면, 그냥 난 호출하면서 해당 공통 함수를 호출하면 끝이다.
public int someMethod(CalculateCommand calculateCommand) {
CalculateType calculateType = calculateCommand.getCalculateType();
int num1 = calculateCommand.getNum1();
int num2 = calculateCommand.getNum2();
int result = calculateType.calculate(num1, num2);
return result;
}
매우 심플해졌다.ㅎㅎㅎ,,,,
정리를 해보자.
1. 사전에 미리 return을 해보자.
2. Enum 안에 생성자로 넣어보자.
3. 생성 시점에 유효성 검사를 미리 해보자.
'Java' 카테고리의 다른 글
collect(Collectors.toList()) vs Stream.toList() (0) | 2023.11.27 |
---|---|
Try with resources (0) | 2023.10.16 |
SOLID(의존성을 잘 관리해보자!!) (0) | 2023.09.25 |
Stream (0) | 2023.09.03 |