티스토리 뷰
else 예약어를 쓰지 않는다
말 그대로 else 예약어를 사용하지 않고 코드를 작성해보라는 지침이다. switch/case 문을 사용하는 것도 허용하지 않는다.
숨은 의미
이 원칙의 제시의도는 한 메소드에서 발생하는 분기문을 줄이자는 것이다. 분기문을 많이 가지고 있는 메소드는 많은 기능을 가지고 있을 확률이 높다. 메소드를 분리하거나, 객체지향적인 구조를 적용해 분기문을 줄일 수 있다. 이는 결국 설계관점에서의 개선을 의미한다.
else 키워드는 '조건을 만족하지 않을 때' 를 전제하고 시작한다. 가독성이 떨어질 수 밖에 없다. if 조건을 만족하지 않는 모든 경우 를 의미하기 때문에, 코드를 읽을 때 양 쪽을 함께 생각해야 한다. 오류가 발생할 확률도 높다.
예시
public String getProductName(String productCode) {
String productName = "";
if ("1".equals(productCode)) {
productName = "할부";
} else if ("2".equals(productCode)) {
productName = "리스";
} else if ("3".equals(productCode)) {
productName = "담보";
} else {
productName = "신용";
}
return productName;
}
위 코드는 else 문 기반의 코드를 작성한 예시이다. 코드의 길이가 길지 않아서 가독성이 나쁘지 않다고 느낄 수 있다. 그러나 실무에서는 이보다 훨씬 복잡한 코드를 작성하게 되는 경우가 많다. 이 메소드는 productName 변수의 리턴이 블록 맨 끝에서 이루어진다. 모든 조건문의 분기를 다 확인하기 전까지는 메소드가 어떻게 동작할 지 확신하기가 어렵다.
비즈니스적 구조로 보았을 때에도 문제가 있다. 마지막 else 문은 "1", "2", "3" 외의 값이 유입되었을 때 productName 변수에 "신용" 이라는 name을 매핑해서 리턴해준다. else문은 위 경우를 만족하지 않는 모든 경우를 의미하기 때문에 설계 의도를 해칠 수 있다. 상식적으로 productCode와 productName은 1:1 의 관계를 가질 것이다. 그러나 이 구조는 "신용" 이라는 상품이름에 한해서는 code와 name이 n:1 관계를 가진다고 표현해버린다.
early return 구조를 적용해보자
else문 제거의 효과적인 방법은 elarly return 구조를 적용하는 것이다.
public String getProductName(String productCode) {
if ("1".equals(productCode)) {
return "할부";
}
if ("2".equals(productCode)) {
return "리스";
}
if ("3".equals(productCode)) {
return "담보";
}
if ("4".equals(productCode)){
return "신용";
}
throw new IllegalArgumentException("존재하지 않는 상품입니다.");
}
early return - throw exception 구조를 적용하면 메소드의 가독성을 높일 수 있다. 개발자가 의도한 조건을 만족(상품코드 일치)하는 경우 메소드는 자신의 역할(상품이름 리턴)을 수행하고 종료한다. 코드를 읽을 때 의도한 조건을 생각하고 읽어주기만 하면 된다. 먼저 리턴을 하기 때문에 메소드를 끝까지 확인하지 않아도 된다.
if - return 기반으로 코드를 작성하면, 코드를 읽을 때 의식의 흐름이 메소드의 동작을 위한 '참' 조건을 기반으로 일어난다. 'A인 경우'를 기반으로 생각하는 것이 'A가 아닌경우' 를 기반으로 생각하는 것 보다는 훨씬 명확하고 쉽다.
한 가지 더 early return 패턴으로 코드를 작성하면 불필요한 지역변수의 생성을 막을 수 있다. 위 예시코드에서는 productName 이라는 지역변수를 선언하고 코드를 작성했는데, 이 경우에 중간에 지역변수 값이 변경될 수 있다는 예외상황이 언제든지 존재할 수 있다. 결국 분석해야할 코드량을 늘리게 된다.
객체지향 설계
분기점이 많아진다는 것은, 클래스의 구조를 변경해야 함을 의미할 확률이 높다. 위 로직을 enum을 활용해서 다음과 같이 변경할 수도 있다.
public enum LoanProduct {
INSTALLMENT("1", "할부"),
LEASE("2", "리스"),
COLLATERAL("3", "담보"),
CREDIT("4", "신용")
;
private String productCode;
private String productName;
LoanProduct(String productCode, String productName) {
this.productCode = productCode;
this.productName = productName;
}
public static String productName(String productCode) {
return Arrays.stream(values())
.filter(bankingProduct -> bankingProduct.productCode.equals(productCode))
.findFirst()
.orElseThrow( () -> new IllegalArgumentException("상품이 존재하지 않습니다."))
.name();
}
}
public String getProductName(String productCode) {
return LoanProduct.productName(productCode);
}
enum을 통해서 상품명을 통합적으로 관리할 수가 있게 되었다. 상품정보의 변경이 일어나더라도, enum 내부에서 모두 처리할 수 있는 유연한 코드가 된다.
한 클래스의 여러 메소드에서 같은 유형의 분기가 발생하는 패턴이 반복된다면, 클래스를 분리해 볼 수 있는 신호가 왔다고 생각할 수 있다. 분기문을 줄이기 위해 클래스를 분리하는 작업에 대해서는 별도의 포스팅으로 작성해 보겠다.
참고자료 (소트웍스 엔솔러지)
'프로그래밍 > 클린코드 & 리팩토링' 카테고리의 다른 글
[객체지향 생활체조 원칙] 규칙 5. 줄여쓰지 않는다 (0) | 2021.09.05 |
---|---|
[객체지향 생활체조 원칙] 규칙 4. 한 줄에 점을 하나만 찍는다 (0) | 2021.08.29 |
[객체지향 생활체조 원칙] 규칙 3. 모든 원시값과 문자열을 포장한다 (3) | 2021.08.28 |
[객체지향 생활체조 원칙] 규칙 1. 한 메서드에 오직 한 단계의 들여쓰기만 한다 (0) | 2021.08.14 |
매직넘버, 매직 리터럴 (2) | 2021.08.07 |
- Total
- Today
- Yesterday