본문 바로가기
Engineering WIKI/Book

리팩토링 2판 - Chapter 10 (조건부 로직 간소화)

by wonos 2022. 10. 20.

조건문이 복잡해지는 경우 다루기가 굉장히 까다로워지는데 이것을 해결하기 위한 다양한 방법들을 제안합니다.

10.3 중첩 조건문을 보호 구문으로 바꾸기

함수 중간에 guard 로 로직을 중단하는 게 의도를 더 드러낼 수 있는 경우가 있습니다. Swift에서는 명시적으로 guard라는 구문이 있어서 편리하게 사용할 수 있는데, 언어 레벨에서 구문을 지원하기 때문에 많은 개발자들이 더 활발하게 사용하게 되는 것 같습니다.

  • 중첩 조건문을 보호 구문으로 바꾸기 리팩터링의 핵심은 의도를 부각하는데 있다. 나는 if-then-else 구조를 사용할 때 if절과 else절에 똑같은 무게를 두어, 코드를 읽는 이에게 양 갈래가 똑같이 중요하다는 뜻을 전달한다. 이와 달리, 보호 구문은 "이건 이 함수의 핵심이 아니다. 이 일이 일어나면 무언가 조치를 취한 후 함수에서 빠져나온다"라고 이야기한다. p.361
  • 코드에서는 명확함이 핵심이다. p.361
  • 어떤 원칙을 약간 위배한다 하더라도 명확함을 드러낼 수 있는 방향으로 코드를 작성하는 것도 생각해보는 것이 좋겠습니다.

10.5 특이 케이스 추가하기

예전부터 매력적이라고 생각하는 방법인데요. 예외 케이스를 객체로 표현해버리는 방법입니다.

코드 군데군데 동일한 예외를 체크하는 부분이 있다면 예외 자체를 객체로 만들 수 있습니다.

아래 예제가 좋은지는 모르겠지만 이런게 있다고 치면

if customer == "미확인 고객" {
    customerName = "거주자"
}

아래와 같이 다형성을 이용해서 처리할 수 있습니다.

class UnknownCustomer: Customer {
    var name: String { "거주자" }
}

이러한 패턴을 특이 케이스 패턴(Special Case Pattern)이라고 합니다.

특별히 null 케이스를 다루는 건 널 객체 패턴(Null Object Pattern) 이라고 부릅니다.

흥미로웠던 부분은 특이 케이스의 프로퍼티가 객체인 경우에 해당 객체도 특이 케이스로 처리해줄 수 있다는 점 이었습니다.

말로 하니 어려운데요 예제를 한번 보겠습니다.

let weeksDelinquent = isUnknown(customer) ? 0 : customer.paymentHistory.weeksDelinquentInLastYear

위와 같은 코드가 있을 때 customer를 특이 케이스로 처리할 수 있습니다.

그런데 paymentHistory는 어떻게 다루는 게 좋을까요?

동일한 방식으로 NullPaymentHistory를 만들어서 weeksDelinquentInLastYear가 0을 반환하도록 만들 수 있습니다.

class UnknownCustomer: Customer {
    var paymentHistory: PaymentHistory {
        return NullPaymentHistory()
    }
}

class NullPaymentHistory: PaymentHistory {
    var weeksDelinquentInLastYear: Int {
    	return 0
    }
}

이런 식으로 처리하면 클라이언트 입장에서는 간단하게 예외 체크를 하지 않아도 됩니다.

let weeksDelinquent = customer.paymentHistory.weeksDelinquentInLastYear

깔끔하죠?!

10.6 어서션 추가하기

  • 어서션이 실패했다는 건 프로그래머가 잘못했다는 뜻이다. p.404
  • 어서션을 오류 찾기에 활용하라고 추천하는 사람도 왕왕 본다. 물론 좋은 일이긴 하지만 어서션의 쓰임은 여기서 끝나지 않는다. 어서션은 프로그램이 어떤 상태임을 가정한 채 실행되는지를 다른 개발자에게 알려주는 훌륭한 소통 도구인 것이다. p.404
  • 어서션을 오류를 찾기 위한 장치로 활용할 수 도 있지만, 소통 도구로 활용할 수도 있습니다.