Change Function Declaration, 함수 선언 바꾸기에 대해 알아보자.
요약

코드
function circum(radius) { ... }function circumference(radius) { ... }배경
- 함수는 프로그램을 작은 부분으로 나누는 주된 수단이다.
- 함수 선언은 각 부분이 서로 맞물리는 방식을 표현한다.
- 그러므로 실질적으로 소프트웨어 시스템의 구성 요소를 조립하는 연결부 역할을 한다.
- 건축과 마찬가지로 소프트웨어도 이러한 연결부에 상당히 의존한다.
- 연결부를 잘 만들어두면 새로운 부분을 추가하기가 쉬워진다.
- 이런 연결부에서 가장 중요한 요소는 함수의 이름이다.
- 함수 이름만 좋으면 뭘 하는 건지 단번에 알 수 있다.
- 하지만 잘못된 이름을 봐도 그냥 두고 싶은 유혹에 빠진다. 그냥 이름이니까.
- 하지만 그냥 둔다면 혼란을 두는 결과를 낳는다. 프로그램의 영혼을 위해서라도 이러한 속삭임에 넘어가지말자.
이름이 안나올 때 함수의 동작을 주석으로 써보자. 그 주석에서 힌트를 얻어 이름으로 대체하자.
- 함수의 매개변수도 중요하다.
- 매개변수를 함수가 외부 세계와 어우러지는 방식을 정의한다.
- 어느정도 레벨의 추상화로 매개변수를 받는지에 따라 함수의 활용범위가 달라진다.
- 클래스로 받을 것인가? String으로 받을 것인가?
- 매개변수를 올바르게 선택하는 것은 단순히 규칙 몇개로 표현할 수 없다.
- 정답이 없는 문제이기 때문이다.
절차
간단한 절차
- 매개변수를 제거하려거든 먼저 함수 본문에서 제거 대상 매개변수를 참조하는 곳은 없는지 확인한다.
- 메서드 선언을 원하는 형태로 바꾼다.
- 기존 메서드 호출문을 새로운 형태로 맞춘다.
- 테스트한다.
- 변경할 게 둘 이상(매개변수 제거, 메서드 선언 바꾸기)이라면 하나씩 나눠서 하자.
마이그레이션 절차
- 이어지는 추출 단계를 수월하게 만들어야 한다면 함수 본문을 적절히 리팩토링한다.
- 함수 본문을 새로운 함수로 추출한다.
- 이 경우 추출할 이름이 이전 함수와 이름이 같을 수 있는데, 그럴 경우 임시 이름을 선정하자.
- 추출한 함수에 매개변수를 추가해야 한다면 간단한 절차를 따라 추가한다.
- 테스트한다.
- 기존 함수를 인라인한다.
- 이름을 임시 이름에서 원래 이름으로 변경한다.
- 테스트한다.
따라하기
함수 이름 바꾸기(간단한 절차)
function circum(radius) {
return 2 * Math.PI * radius;
}function circumference(radius) {
return 2 * Math.PI * radius;
}- 함수 이름을 너무 축약한 케이스이다.
- 요즘을 IDE에서 찾아주니 이런건 금방할 수 있다.
함수 이름 바꾸기(마이그레이션 절차)
function circum(radius) {
return 2 * Math.PI * radius;
}function circum(radius) {
return circumference(radius);
}
function circumference(radius) {
return 2 * Math.PI * radius;
}- 이전에 사용하는 메서드인
circum의 내부 구현만을 대체하여 하위 버전 호환성을 유지했다. - 공개 API와 같은 경우에는 이렇게 처리해야 한다.
- deprecated로 처리해두고, 특정 시점에 제거하는 것이 좋다.
매개변수 추가하기
- 도서 관리 프로그램의 Book 클래스에 예약 기능이 구현되어 있다고 하자.
// Book Class
addReservation(custormer) {
this._reservations.push(customer);
}- 그런데 예약시 우선순위 큐를 지원하라는 요구사항이 추가되었다.
- 매개변수로 값을 받아 분기쳐서 넣을 예정이다.
- 그렇다면
addReservation이 얼마나 많은 곳에서 사용하는지 확인해야 한다. - 적으면 그냥 다 바꾸고, 많다면 마이그레이션 절차를 사용해야 한다. 두번째 상황이라고 해보자.
// Book Class
addReservation(customer) {
this.zz_addReservation(customer, false);
}
zz_addReservation(customer, isPriority) {
this._reservations.push(customer);
}- 일단 함수를 추출해두자.
- 같은 이름을 쓸 수 없어 임시 이름으로 붙였다.
// Book Class
addReservation(customer) {
this.zz_addReservation(customer, false);
}
zz_addReservation(customer, isPriority) {
this._reservations.push(customer);
}- 새 함수의 선언문과 호출문에 원하는 매개변수를 추가한다.
- 다음으로는
zz_addReservation호출부를 함수 인라인한다.
매개변수를 속성으로 바꾸기
- 이번엔 좀더 복잡한 상황을 가정해보자
function inNewEngland(aCustomer) {
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(aCustomer.address.state);
}
// 호출문
const newEnglanders = someCustomers.filter(c => inNewEngland(c));- 이 함수는 매개변수로 받은 고객이 뉴잉글랜드에 사는지 여부를 확인한다.
- 이 경우
customer자체를 매개변수로 받고 있어 커플링이 심하다. - 주 식별 코드만 매개변수로 받는다면 의존성이 제거되어 더 넓은 문맥에서 사용가능하다.
function inNewEngland(aCustomer) {
const stateCode = aCustomer.address.state;
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(stateCode);
}- 간단하게 리팩터링 해두고, 함수 추출하기를 사용하자.
function inNewEngland(aCustomer) {
const stateCode = aCustomer.address.state;
return xxNEWinNewEngland(stateCode);
}
function xxNEWinNewEngland(stateCode) {
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(stateCode);
}- 이제 변수를 다시 인라인하자.
function inNewEngland(aCustomer) {
return xxNEWinNewEngland(aCustomer.address.state);
}
function xxNEWinNewEngland(stateCode) {
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(stateCode);
}- 함수 인라인하기를 적용하자. 그리고 새 함수로 교체하자.