본문 바로가기

Clean Code

[리팩터링 2판 - 실전편] 10장 조건부 로직 간소화


마틴파울러 리팩터링 2판 정주행 스터디 - 실전편

 

10.1 조건문 분해하기 Decompose Conditional

복잡한 조건부 로직은 프로그램을 복잡하게 만드는 가장 흔한 원흉에 속한다. 긴 함수는 그 자체로 읽기가 어렵지만, 조건문은 그 어려움을 한층 가중시킨다.

조건을 검사하고 그 결과에 따른 동작을 표현한 코드는 무슨 일이 일어나는지는 이야기해주지만 '왜'일어나는지는 제대로 말해주지 않을 때가 많은 것이 문제다.

거대한 코드 블록이 주어지면 코드를 부위별로 분해한 다음 해체된 코드 덩어리들을 각 덩어리의 의도를 살린 이름의 함수 호출로 바꿔주자. 그러면 전체적인 의도가 더 확실히 드러난다.

 

10.2 조건식 통합하기 Consolidate Conditional Expression

비교하는 조건은 다르지만 그 결과로 수행하는 동작은 똑같은 코드들이 더러 있는데, 어차피 같은 일을 할 거라면 조건 검사도 하나로 통합하는게 낫다.

'&&' 연산자와 '||' 연산자를 사용하면 여러 개의 비교 로직을 하나로 합칠 수 있다.

조건부 코드를 통합하는 게 중요한 이유는 첫째, 여러 조각으로 나뉜 조건들을 하나로 통합함으로써 내가 하려는 일이 명확해진다. 읽는 사람은 독립된 검사들이 우연히 함께 나열된 것으로 오해할 수 있다.

두 번째 이유는 이 작업이 복잡한 조건식을 이 작업이 함수 추출하기까지 이어질 가능성이 높기 때문이다. 복잡한 조건식을 함수로 추출하면 코드의 의도가 훨씬 분명하게 드러나는 경우가 많다. 함수 추출하기는 '무엇'을 하는지 기술하던 코드를 '왜' 하는지를 말해주는 코드로 바꿔주는 효과적인 도구임을 기억하자.

 

10.3 중첩 조건문을 가드문으로 바꾸기 Replace Nested Conditional with Guard Clauses

참인 경우와 거짓인 경우 모두 정상 동작으로 이어지는 형태라면 if/else 절을 사용한다. if절과 else절에 똑같은 무게를 두어, 코드를 읽는 이에게 양 갈래가 똑같이 중요하다는 뜻을 전달한다.

한쪽만 정상이라면 비정상 조건을 if에서 검사한 다음, 조건이 참이면(비정상이면) 함수에서 빠져나온다. 이 형태를 흔히 가드문(gaurd clause)이라고 한다.

중첩 조건문을 가드문으로 바꾸는 핵심은 의도를 부각하는 데 있다. 가드문은 "이건 이 함수의 핵심이 아니다. 이 일이 일어나면 무언가 조치를 취한 후 함수에서 빠져나온다"라고 이야기한다.

 

 

10.4 조건부 로직을 다형성으로 바꾸기 Replace Conditional with Polymorphism

다형성은 객체 지향 프로그래밍의 핵심이다.

조건문 구조를 그대로 둔 채 조건들을 분리해낼 수 있지만, 클래스와 다형성을 이용하면 더 확실하게 분리할 수 있다.

case별로 클래스를 하나씩 만들어 공통 switch 로직의 중복을 없앨 수 있다.

기본 동작은 슈퍼클래스로 넣어서 변형 동작에 신경 쓰지 않고 기본에 집중하게 하고 변형 동작을 뜻하는 case들을 각가의 서브클래스로 만드는 방법도 있다.

 

10.5 특이케이스 추가하기 Introduce Special Case

데이터 구조의 특정 값을 확인한 후 똑같은 동작을 수행하는 코드가 곳곳에 등장하는 경우가 더러 있는데, 흔히 볼 수 있는 중복 코드 중 하나이다.

특수한 경우의 공통 동작을 요소 하나에 모아서 사용하는 특이케이스 패턴(Special Case Pattern)은 이럴 때 적용하면 좋은 메커니즘이다. 

단순히 데이터를 읽기만 해야한다면 반환할 값들을 담은 리터럴 객체 형태로 준비하고, 동작을 수행해야 한다면 필요한 메서드를 담은 객체를 생성하면 된다. 

null을 특이 케이스로 처리하는 패턴을 널 객체 패턴(Null Object Pattern)이라고도 한다. 

 

10.6 어서션 추가하기 Introduce Assertion

제곱근 계산은 입력이 양수일 때만 정상 동작한다. 이처럼 특정 조건이 참일 때만 제대로 동작하는 코드 영역이 있을 수 있다. 이런 가정이 코드에 항상 명시적으로 기술되어 있으면 알고리즘을 보고 해석해서 알아내야할 때도 있다.

어서션(assertion)을 이용하면 이 내용을 코드 자체에 명시적으로 삽입해놓을 수 있다. 어서션은 어떤 상태임을 가정한 채 실행되는지를 다른 개발자에게 알려주는 훌륭한 소통 도구인 것이다.

어서션은 항상 참이라고 가정하는 조건부 문장으로, 어서션에 실패했다는 건 프로그래머가 잘못했다는 뜻이다. 또, 어서션을 작성할 때는 어서션이 있고 없고가 프로그램 기능의 정상 동작에 아무런 영향을 주지 않도록 작성되어야 한다.

테스트 코드가 있다면 어서션의 디버깅 용도로서의 효용은 줄어든다. 하지만 소통 측면에서의 어서션은 여전히 매력적이다. 소통 수단으로서의 가치 때문에, 추적하던 버그를 잡은 뒤에도 어서션을 코드에 남겨두곤 한다.

 

10.7 제어 플래그를 탈출문으로 바꾸기 Replace Control Flag with Break

제어 플래그(Control Flag)란, 제어 흐름을 변경하는 데 사용되는 변수를 말한다. visited, found 같은 boolean 변수를 예로 들 수 있다.

제어플래그는 리팩터링으로 충분히 간소화할 수 있음에도 복잡하게 작성된 코드에서 흔히 나타난다. 주로 반복문 안에서 어딘가에서 값을 계산해 제어 플래그에 설정한 후 다른 어딘가의 조건문에서 검사하는 형태로 쓰인다. break문이나 continue 문 활용에 익숙하지 않은 사람이나 return문을 하나로 유지하고자 하는 사람이 작성하는 편이다.