리팩터링 (10장 조건부 로직 간소화)

박주진·2021년 12월 5일
0

리팩터링

목록 보기
6/7

아래 내용은 리팩터링 2판 내용과 한달한권 읽기 강의를 기반하여 정리한 글입니다.

조건문 분해하기

  • 조건문은 함수를 이해하기 더욱 힘들게 만든다. 왜냐하면 어떻게는 나열이 되어 있지만 무엇인지/왜 일어나는 지는 제대로 말해주지 않을때가 많기 때문이다.
  • 거대한 코드 블럭을 부위별로 분해한 다음 의도를 살린 이름을 가진 함수로 바꿔주자.
//전
if(!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))
  charge = quantity * plan.summerRate;
else
 charge = quantity * plan.regularRate + plan.regularServiceCharge;
 
//후
if(summer())
  charge = summerCharge();
else
  charge = regularCharge();

활용될 수 있는 악취들

  • 긴 함수

조건식 통합하기

  • 비교하는 조건은 다르지만 그 결과로 같은 일을 하는 여러조건은 하나로 통합하라.
  • 여러조건이 통합되면 하려는 일이 명확해진다. 이는 함수 추출하기 까지 이어져 어떻게 하는지가 아닌 목적이 들어나는 코드로 변경할 수 있다.
//전
if(anEmployee.seniority < 2) return 0;
if(anEmployee.monthsDisabled > 12) return 0;
if(anEmployee.isPartTime) return 0;

//후
if(isNotEligibleForDisability()) return 0;

function isNotEligibleForDisability() {
 return ( (anEmployee.seniority<2)
 			|| (anEmployee.monthsDisabled > 12)
            || (anEmployee.isPartTime));
}

활용될 수 있는 악취들

  • 책에서 언급된 악취는 없음 하지만 굳이 고르자면 긴 함수

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

  • 조건문에서 참인 경로와 거짓인 경로 모두 정상 동작으로 이어지는 형태는 if else절에 똑같은 무게를 두어 코드를 읽는 이에게 양갈래 똑같이 중요하다는 뜻을 전달한다.
  • 조건문에서 한쪽만 정상 동작으로 이어지는 형태는 보호구문(비정상 조건은 if에서 모두 검사하여 함수에서 빠져나온는 형태)으로 변경한다.
//전
function getPayAmount() {
	let result;
    if(isDead)
    	result = deadAmount();
    else {
    	if(isSeparated)
          result = separatedAmount();
        else {
        	if(isRetired)
            	result = retiredAmount();
            else
             	result = normalPayAmount();
        
        }
    
    }
}
return result;

//후(보호구문 적용결과)
function getPayAmount() {
 if(isDead) return deadAmount();
 if(isSeparated) return separatedAmount();
 if(isRetired) return retiredAmount();
 return normalPayAmount();
}

활용될 수 있는 악취들

  • 책에 언급된 악취는 없음

조건부 로직을 다형성을 바꾸기

  • 타입을 기준으로 분기하는 switch문이 여러곳에서 사용되고 있다면 다형성으로 활용하는 것을 고려하라.
  • 기본 동작을 위한 case문과 그 변형 동작으로 구성된 로직이 있다면 수퍼 클래스와 서브클래스로 구조로 변경을 고려해보라. (슈퍼 클래스에서는 기본동작에 집중 서브클래스에서 각각의 변형 동작을 구현)
  • 모든 조건문을 다형성을 대체하지 말라.
//전
switch(bird.type) {
 case 'EuropeanSwallow'
  return "보통이다";
 case 'AfricanSwallow'
  return (bird.numberOfCoconuts > 2) ? "지쳤다" : "보통이다";
 case 'NorwegianBlueParrot'
  return (bird.voltage > 100) ? "그을렸다" : "예쁘다";
 default:
  return "알 수 없다";
}

//후
class EuropeanSwallow {
 get plumage() {
  return "보통이다";
 }
 
 class AfricanSwallow {
 get plumage() {
  return (bird.numberOfCoconuts > 2) ? "지쳤다" : "보통이다";
 }
 
 
 class NorwegianBlueParrot {
 get plumage() {
  return (bird.voltage > 100) ? "그을렸다" : "예쁘다";
 }
}

활용될 수 있는 악취들

  • 기본형 집착
  • 긴 함수
  • 반복되는 switch

특이 케이스 추가하기

  • 특수한 경우에 대한 중복된 코드가 존재한다면 이를 하나로 모아서 사용하는 특이 케이스 패턴을 적용할 수 있다.
  • 보통 null이 특이 케이스로 처리해야 할 때가 많다. 그래서 null 객체 패턴이라고도 불린다.
// 전
function blahblah() {
....
let customerName;
if(aCustomer === "미확인 고객") customerName = "거주자";
else customerName = aCustomer.name;
}

//후
function blahblah() {
...
 return aCustomer.name
}

class UnkwonCustomer {
	get name() {return "거주자";}
}

활용될 수 있는 악취들

  • 임시 필드

어서션 추가하기

  • 어서션은 프로그램이 어떤 상태임을 가정한 채 실행되는지를 다른 개발자에게 알려주는 훌륭한 소통 도구이다.
  • 테스트 코드가 있다면 어서션의 디버깅 요소로서의 효용은 줄어든다.
  • 남발하지 말고 반드시 참이여야 하는 것에만 사용하라.
  • 프로그래머가 일으킬만한 오류에만(내부코드에서 생성되는 값)어서션을 사용하라. 외부에서 읽어오는 값이라면 예외처리로 대응해야 한다.
// 전
if( this.discountRate) {
 base = base - (this.discountRate * base);
}


// 후
assert(this.discountRate >= 0)
if(this.discountRate) {
 base = base - (this.discountRate * base);
}

활용될 수 있는 악취들

  • 없음

제어 플래그를 탈출문으로 바꾸기

  • 제어 플래그란 코드의 동작을 변경하는데 사용되는 변수이며 주로 조건문을 검사하는 형태로 쓰인다.
  • 주 서식지는 반복문 안이다.
//전
for(const p of people) {
 if(!found) {
 	if(p === "조커"){
    	sendAlert();
        found = true;
    }
    `if(p === "사루만"){
    	sendAlert();
        found = true;
    }
 }
}

//후
for(const p of people) {
 if(!found) {
 	if(p === "조커"){
    	sendAlert();
        return;
    }
    
     if(p === "사루만"){
    	sendAlert();
        return;
    }
 }
}
//더 나은 버젼
if (people.some(p => ["조커","사루만"].includes(p)))) sendAlert();

활용될 수 있는 악취들

  • 없음

0개의 댓글