[Refactoring] # 함수 옮기기

mechaniccoder·2021년 10월 26일
0
post-thumbnail

좋은 소프트웨어의 핵심은 모듈성이라고 책에 나와있다. 모듈에 대해서는 최소한의 것만을 이해하고 그와 관련된 로직들은 내부에 깊이 숨겨두는 것이다.

그 모듈에 정의되어 있는 함수는 어떠한 컨텍스트 안에서 존재할텐데, 만약 다른 모듈을 더 많이 참조하고 있다면 이 함수를 옮겨주는 것이 타당하다.

이번 챕터는 한마디로 함수의 올바른 컨텍스트를 정하는 것이라고 해도 무방할 것 같다.

Code

class example

class Account {
  get bankCharge() {
    let result = 4.5;
    if (this._daysOverdrawn > 0) result += this.overdraftCharge;
    return result;
  }
  get overdraftCharge() {
    if (this.type.isPremium) {
      const baseCharge = 10;
      if (this.daysOverdrawn <= 7) return baseCharge;
      else return baseCharge + (this.daysOverdrawn - 7) * 0.85;
    } else {
      return this.daysOverdrawn * 1.75;
    }
  }
}

// 계좌 종류에 따라서 이자를 다르게 하기 위해, overdraftCharge메서드를 AccountType 클래스로 옮기자.
class AccountType {
  overdraftCharge(daysOverdrawn) {
    if (this.type.isPremium) {
      const baseCharge = 10;
      if (daysOverdrawn <= 7) return baseCharge;
      else return baseCharge + (daysOverdrawn - 7) * 0.85;
    } else {
      return daysOverdrawn * 1.75;
    }
  }
}

class Account {
  //...

  // Account에서는 위임메서드를 통해서 결과값을 가져오면 된다.
  get overdraftCharge() {
    return this.type.overdraftCharge(this.daysOverdrawn);
  }
}
module.exports = {};

function example

function trackSummary(points) {
  const totalTime = calculateTime();
  const totalDistance = calculateDistance();
  const pace = totalTime / 60 / totalDistance;
  return {
    time: totalTime,
    distance: totalDistance,
    pace,
  };

  function calculateDistance() {
    let result = 0;
    for (let i = 0; i < points.length; i++) {
      result += distance(points[i - 1], points[i]);
    }
    return result;
  }

  function distance(p1, p2) {}
  function radians(degrees) {}
  function calculateTime() {}
}

// calculateDistance를 최상위로 옮겨보자.
function top_calculateDistance(points) {
  let result = 0;
  for (let i = 0; i < points.length; i++) {
    result += distance(points[i - 1], points[i]);
  }
  return result;
}

// disatnce와 radians의 경우 trackSummary라는 최상위 함수 컨텍스트에 있는 어떤 것도
// 사용하지 않기 때문에 차라리 calculateDistacne함수 안으로 옮긴다.
function top_calculateDistance(points) {
  let result = 0;
  for (let i = 0; i < points.length; i++) {
    result += distance(points[i - 1], points[i]);
  }
  return result;

  function distance(p1, p2) {}
  function radians(degrees) {}
}

// 원본함수에서 복사한 함수를 호출하자.
function trackSummary(points) {
  const totalTime = calculateTime();
  const totalDistance = calculateDistance();
  const pace = totalTime / 60 / totalDistance;
  return {
    time: totalTime,
    distance: totalDistance,
    pace,
  };

  function calculateDistance() {
    top_calculateDistance(points);
  }

  function calculateTime() {}
}

// 교체해주자. totalDistance로 이름을 바꾸고 변수를 인라인 해주자.
function trackSummary(points) {
  const totalTime = calculateTime();
  const pace = totalTime / 60 / totalDisatnce(points);
  return {
    time: totalTime,
    distance: totalDisatnce(points),
    pace,
  };

  function calculateTime() {}
}

// distance나 radians은 top_calculateDisatnce의 함수에 의존하고 있지 않으므로,
// 마찬가지로 최상위 함수로 옮긴다.
// 중첩함수는 데이터에 대한 결합과 의존성이 생기므로 되도록이면 피하자!
function trackSummary() {}
function totalDisatnce() {}
function distance() {}
function radians() {}

Reference

Refactoring: Improving the Design of Existing Code (2nd Edition) p278-288.

느낀점

객체지향의 연장선에 있는 것 같다는 생각이 많이 들었다. 객체가 가지는 책임이 있고 그와 관련된 오퍼레이션들을 잘 모아둬서 응집력있는 코드를 작성하는 리팩토링 기법이라고 느꼈다. 특히 클래스를 활용한 예제에서는 객체의 역할에 따라서 public메서드로 메세지를 받을 수 있도록 코드를 수정했다는 것을 알 수 있었다.

profile
세계 최고 수준을 향해 달려가는 개발자입니다.

0개의 댓글