티스토리 뷰

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 내부에서 모두 처리할 수 있는 유연한 코드가 된다.

 

 한 클래스의 여러 메소드에서 같은 유형의 분기가 발생하는 패턴이 반복된다면, 클래스를 분리해 볼 수 있는 신호가 왔다고 생각할 수 있다. 분기문을 줄이기 위해 클래스를 분리하는 작업에 대해서는 별도의 포스팅으로 작성해 보겠다.

 

참고자료 (소트웍스 엔솔러지)

https://book.naver.com/bookdb/book_detail.nhn?bid=5441199

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday