10. 조건부 로직 간소화
- 조건문 분해하기, 중복 조건식 통합하기
- 복잡한 조건문인 경우나 논리적 조합을 명확하게 다듬는 경우
- 중첩 조건문을 보호 구문으로 바꾸기, 조건부 로직을 다형성으로 바꾸기
- 핵심 로직 전 무언가를 검사하거나, 같은 분기 로직이 자주 등장하는 경우
- 특이 케이스 추가하기(널 개체 추가하기), 어서션 추가하기
- 특이 케이스 처리시
- 제어 플래그를 탈출문으로 바꾸기
- 제어 플래그를 이용해 코드 동작 흐름을 바꾸는 경우
#
10.1 조건문 분해하기#
배경- 복잡한 조건부 로직은 프로그램을 만들게 합니다.
- 거대한 코드 블럭은 코드를 부위별로 분해한 다음 해체된 코드 덩어리들을 각 덩어리의 의도를 살인 이름의 함수 호출을 했습니다.
#
절차- 조건식과 그 조건식에 달린 조건절 각각을 함수로 추출했습니다.
#
예시- 리팩터링 전 코드
- 리팩터링 후 코드
#
10.2 조건식 통합하기#
배경- 비교하는 조건은 다르나 결과로 수행하는 동작이 똑같은 경우에 할 수 있습니다.
- 조건부 코드를 통합하는 경우는 두가지 이유가 있습니다.
- 여러 조각으로 나뉜 조건들을 하나로 통합함으로써 하는 일이 명확해집니다.
- 함수 추출하기 까지 이어질 가능성이 높습니다.
#
절차- 해당 조건식을 모두에 보수효과가 없는지 확인합니다.
- 조건문 두 개를 선택하여 두 조건문의 조건식들을 논리 연산자로 결합합니다.
- 테스트합니다.
- 조건을 하나만 남을 때까지 2~3 과정을 반복합니다.
- 하나로 합쳐진 조건식을 함수로 추출할지 고민합니다.
#
예시앞의 예시 말고도 다음과 같이 사용할 수 있습니다.
#
10.3 중첩 조건문을 보호 구문으로 바꾸기#
배경- 조건문은 주로 두가지 형태로 쓰입니다.
- 참인 경로와 거짓인 경로 모두 정상 동작으로 이어지는 형태
- 한쪽만 정상인 형태
- 중첩 조건문을 보호 구문으로 바꾸기 리팩터팅의 핵심은 의도를 부각하는 데 의미가 있습니다.
#
절차- 교체해야 할 조건 중 가장 바깥 것을 선택하여 보호 구문으로 바꿉니다.
- 테스트합니다.
- 1~2 과정을 필요한 만큼 반복합니다.
- 모든 보호 구문이 같은 결과를 반환한다면 보호 구문들의 조건식을 통합합니다.
#
예시- 최상위 조건부터 보호 구문으로 바꿉니다.
- 테스트 후 다음 조건으로 넘깁니다.
#
조건 반대로 만들기- 조건을 역으로 바꾸면서 보호구문을 추가합니다.
- 조건을 간소화하고, 조건식을 통합합니다.
#
10.4 조건부 로직을 다형성으로 바꾸기#
배경- 복잡한 조건부 로직은 클래스와 다형성을 이용해서 더 확실하게 분리할 수도 있습니다.
#
절차- 다형적 동작을 표현하는 클래스들이 아직 없다면 만들어줍니다. 이왕이면 적합한 인스턴스를 알아서 만들어 반환하는 팩터리 함수도 만듭니다.
- 호출하는 코드에서 팩터리 함수를 사용하게 합니다.
- 조건부 로직 함수를 슈퍼클래스로 옮깁니다.
- 서브 클래스 중 하나를 선택합니다. 서브 클래스에서 슈퍼클래스의 조건부 로직 메서드를 오버라이드합니다. 조건부 문장 중 선택된 서브클래스에 해당하는 조건절을 서브 클래스 메서드로 복사한 다음 적절히 수정합니다.
- 같은 방식으로 각 조건절을 해당 서브클래스에서 메서드로 구현합니다.
- 슈퍼클래스 메서드에서는 기본 동작 부분만 남깁니다. 혹은 슈퍼클래스가 추상 클래스여야 한다면, 이 메서드를 추상으로 선언하거나 서브클래스에서 처리해야 함을 알리는 에러를 던집니다.
#
예시- Bird 클래스로 묶습니다.
- 종별 서브클래스를 만듭니다.
- 코드가 길기 때문에 생략하고, 기존에 switch-case로 되어있는 부분을 오버라이딩해서 수정합니다.
#
10.5 특이 케이스 추가하기#
배경- 특수한 경우의 공통 동작을 요소 하나에 모아서 사용하는 특이 케이스 패턴이 있는데 이때 적용하면 좋습니다.
- 대표적인 예시로 null이 있습니다.
#
절차- 컨테이너에 특이 케이스인지 검사하는 속성을 추가하고 false를 반환하게 합니다.
- 특이 케이스 객체를 만듭니다. 이 객체는 특이 케이스인지를 검사하는 속성만 포함하여, 이 속성은 true를 반환합니다.
- 클라이언트에서 특이 케이스인지를 검사하는 코드를 함수로 추출합니다. 모든 클라이언트가 값을 직접 비교하는 대신 방금 추출한 함수를 사용하도록 고칩니다.
- 코드에 새로운 특이 케이스 대상을 추가합니다. 함수의 반환 값으로 받거나 변환 함수를 적용하면 됩니다.
- 특이 케이스를 검사하는 함수 본문을 수정하여 특이케이스 객체의 속성을 사용하도록 합니다.
- 테스트합니다.
- 여러 함수를 클래스로 묶기나 여러 함수를 변환 함수로 묶기를 적용하여 특이 케이스를 처리하는 공통 동작을 새로운 요소로옮깁니다.
- 아직도 특이 케이스 검사 함수를 이용하는 곳이 남아 있다면 검사 함수를 인라인합니다.
#
예시- 리팩터링 후 코드
- 다른 코드도 생략합니다.
#
10.6 어서션 추가하기#
배경- 특정 조건이 참일 때만 정상적으로 동작하는 코드 영역이 있는데, 이 경우를 명시적으로 작성하면 이해하기 좋아집니다.
- 어셔션을 통해서 제대로 개발하게 되고, 소통 등의 장점을 가집니다.
#
절차- 참이라고 가정하는 조건이 보이면 그 조건을 명시하는 어서션을 추가합니다.
#
예시- if-then으로 수정합니다.
- 어서션을 추가합니다.
- 이는 목적을 위해 setter 메서드에 추가합니다.
#
10.7 제어 플래크를 탈출문으로 바꾸기#
배경- 제어 플래그란 코드의 동작을 변경하는데 사용되는 변수입니다.
- 반복문안에서 좀 보기 안좋은 코드는 break, continue, return 등으로 처리합니다.
#
절차- 제어 플래그를 사용하는 코드를 함수로 추출할지 고민합니다.
- 제어 플래그를 갱신하는 코드 각각을 적절한 제어문으로 바꿉니다. 하나 바꿀때마다 테스트합니다.
- 모두 수정했다면 제어 플래그를 제거합니다.
#
예시- 코드 생략