리팩터링 2판 11장 정리

Study리팩터링 2판스터디javascript

들어가며

본 게시글은 사내 스터디로 공부한 리팩터링 2판 (마틴 파울러) 11장을 정리 & 요약하는 글입니다.

매 챕터마다 돌아가며 설명을 했기 때문에, 본인이 정리한 일부 주제들만 정리했습니다. (나머지 내용은 추후 업데이트 예정)


Chapter 11. API 리팩터링


11.6 질의 함수를 매개변수로 바꾸기

Before

targetTemperature(aPlan)

function targetTemperature(aPlan) {
  currentTemperature = thermostat.currentTemperature;
  // 생략

After

targetTemperature(aPlan, thermostat.currentTemperature)

function targetTemperature(aPlan, currentTemperature) {
  // 생략

배경

  • 함수 안에서 전역 변수를 참조하거나, 제거 대상인 원소를 참조하는 경우에는 (=함수 안에 두기엔 거북한 참조) 매개변수로 바꿀 필요가 있다
  • 모듈을 개발할 때 순수 함수를 따로 구분하고, 가변 원소들을 다루는 로직으로 순수 함수를 감싸는 패턴을 많이 활용한다
  • 순수 함수란?
    • 동일한 인자가 주어졌을 때 항상 동일한 결과를 반환하며, 외부의 상태를 변경하지 않는 함수

효과

  • 똑같은 값을 건네면 매번 똑같은 결과를 내는 함수를 만들기 때문에 다루기 쉽다 (참조 투명성)
  • 참조 투명성이란?
    • 투명하게 전달되는 파라미터외에 다른 외부 세계의 영향을 받지 않는 특징을 의미한다 (= 모든 참조가 투명하다)

적용시점

  • 함수에서 참조 투명하지 않은 원소에 접근하는 경우
  • 책임 소재를 프로그램의 어디에 배정하느냐의 문제는 정답이 있는 것이 아니다, 따라서 상황에 따라 더 나은 쪽으로 개선하기 쉽게 설계해두는게 중요하다

적용 절차

예시

// 실내온도 제어 시스템. 사용자는 온도조절기로 온도를 설정할 수 있지만,
// 목표 온도는 난방 계획에서 정한 범위에서만 선택할 수 있음

// HeatingPlan 클래스
class HeatingPlan {
  get targetTemperature(aPlan) {
    if (thermostat.selectedTemperature > this._max) {
      return this._max;
    } else if (thermostat.selectedTemperature < this._min) {
      return this._min;
    } else {
      return thermostat.selectedTemperature;
    }
  }
}

// 호출자
if (thePlan.targetTemperature > thermostat.currentTemperature) { setToHeat();}
else if (thePlan.targetTemperature < thermostat.currentTemperature) { setToCool();}
else { setOff();}

  1. 변수 추출하기로 이 메서드에서 사용할 매개 변수를 준비
get targetTemperature() {
  const selectedTemperature = thermost.selectedTemperature;  // 변수 추출!
  if (selectedTemperature > this._max) {
    return this._max;
  } else if (selectedTemperature < this._min) {
    return this._min;
  } else {
    return selectedTemperature;
  }
}

  1. 매개변수 값을 구하는 코드를 제외한 나머지를 메서드로 추출하기 쉬워짐
get targetTemperature() {
  const selectedTemperature = thermost.selectedTemperature;
  return this.xxNEWtargetTemperature(selectedTemperature);  // 메서드화
}

// 메서드 추출!
xxNEWtargetTemperature(selectedTemperature){
  if (selectedTemperature > this._max) {
    return this._max;
  } else if (selectedTemperature < this._min) {
    return this._min;
  } else {
    return selectedTemperature;
  }
}

  1. 방금 추출한 변수를 인라인. 원래 메서드에는 단순한 호출만 남도록
// HeatingPlan 클래스
get targetTemperature() {
  // 기존 selectedTemperature 변수 인라인!
  return this.xxNEWtargetTemperature(thermostat.selectedTemperature);
}

  1. 메서드까지 인라인
if (
  thePlan.xxNEWtargetTemperature(thermostat.selectedTemperature) > // 메서드 인라인!
  thermostat.currentTemperature
) {
  setToHeat()
} else if (
  thePlan.xxNEWtargetTemperature(thermostat.selectedTemperature) < // 메서드 인라인!
  thermostat.currentTemperature
) {
  setToCool()
} else {
  setOff()
}

  1. 메서드의 이름을 원래 메서드 이름으로 변경 (접두어 제거)
class HeatingPlan {
  get targetTemperature(selectedTemperature) {    // 메서드 이름 변경!
    if (selectedTemperature > this._max) {
      return this._max;
    } else if (selectedTemperature < this._min) {
      return this._min;
    } else {
      return selectedTemperature;
    }
  }
}

// 호출자
if (
  thePlan.targetTemperature(thermostat.selectedTemperature) > // 메서드 이름 변경!
  thermostat.currentTemperature
) {
  setToHeat();
} else if (
  thePlan.targetTemperature(thermostat.selectedTemperature) <
  thermostat.currentTemperature
) {
  setToCool();
} else {
  setOff();
}

Before & After

/** * 리팩토링 전 */

targetTemperature(aPlan)

function targetTemperature(aPlan) {
  currentTemperature = thermostat.currentTemperature
  // 생략
}

/** * 리팩토링 후 */
targetTemperature(aPlan, thermostat.currentTemperature)

function targetTemperature(aPlan, currentTemperature) {
  // 생략
}


참고




Profile picture
@김하연
4년차 프론트엔드 개발자 입니다. 사용자 경험 개선, 코드의 재사용성, 읽기 쉬운 코드에 집중하여 개발합니다.
AboutGithub LinkedinResume
Loading script...