it 책/리팩터링

함수 추출하기

테오구 2022. 5. 30. 22:21
728x90
  • 반대 리팩토링 : 함수 인라인하기
function printOwing(invocie) {
  printBanner()
  let outstanding = calculateOutstanding()

  // 세부 사항 출력
  console.log(`${invoice.customer}`)
  console.log(`${outstanding}`)
}
function printOwing(invocie) {
  printBanner()
  let outstanding = calculateOutstanding()

  printDetails(outstanding)
}
function printDetails(outstanding) {
  console.log(`${invoice.customer}`)
  console.log(`${outstanding}`)
}

함수 안에 들어갈 코드가 대 여섯 줄을 넘어갈 때부터 않좋은 냄새가 풍긴다.

함수가 짧으면 캐싱하기가 더 쉽기 때문에 컴파일러가 최적화 되는데 유리하다.

절차

  1. 함수를 새로 만들고 목적이 잘 드러나는 이름을 붙입니다.(’어떻게’가 아닌 ‘무엇을’ 하는지가 드러나야 한다.)
  2. 추출한 코드를 원본 함수에서 복사하여 새 함수에 붙여놓는다.
  3. 추출한 코드 중 원본 함수의 지역 변수를 참조하거나 추출한 함수의 유효범위를 벗어나는 변수는 없는지 있다면 매개변수로 전달
  4. 변수를 다 처리했다면 컴파일한다.
  5. 원본 함수에서 추출한 코드 부분을 새로 만든 함수를 호출하는 문장으로 바꾼다.(즉, 추출한 함수로 일을 위임한다.)
  6. 테스트한다.
  7. 다른 코드에 방금 추출한 것과 똑같거나 비슷한 코드가 없는지 살핀다. 있다면 방금 추출한 새 함수를 호출하도록 바꿀지 검토한다.(인라인 코드를 함수 호출로 바꾸기)

예시

function printOwing(invoice) {
  let outstanding = 0
  console.log('====================================')
  console.log('고객 채무')
  console.log('====================================')

  // 미해결 채무(outstanding)를 계산한다.
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  const today = Clock.today
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  )

  // 세부 사항을 출력합니다.
  console.log(`고객명: ${invoice.customer}`)
  console.log(`채무액: ${outstanding}`)
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`)
}

Date.now()처럼 시스템 시간을 알려주는 함수는 직접 호출하지 않는다.

function printOwing(invoice) {
  let outstanding = 0
  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  const today = Clock.today
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  )

  // 세부 사항을 출력합니다.
  console.log(`고객명: ${invoice.customer}`)
  console.log(`채무액: ${outstanding}`)
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`)
}

function printBanner() {
  console.log('====================================')
  console.log('고객 채무')
  console.log('====================================')
}

마찬가지로 세부 사항을 출력하는 코드도 간단히 추출할 수 있다.

function printOwing(invoice) {
  let outstanding = 0
  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  const today = Clock.today
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  )

  // 세부 사항을 출력합니다.
  printDetails()
}

function printDetails() {
  console.log(`고객명: ${invoice.customer}`)
  console.log(`채무액: ${outstanding}`)
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`)
}

지역 변수를 사용할 때

지역 변수와 관련하여 가장 간단한 경우는 변수를 사용하지만 다른 값을 다시 대입하지는 않을 때이다. 이 경우 지역 변수들을 그냥 매개변수로 넘기면 된다.

function printOwing(invoice) {
  let outstanding = 0
  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  const today = Clock.today
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  )

  // 세부 사항을 출력합니다.
  printDetails(invoice, outstanding)
}

function printDetails(invoice, outstanding) {
  console.log(`고객명: ${invoice.customer}`)
  console.log(`채무액: ${outstanding}`)
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`)
}

지역 변수가 데이터 구조라면 똑같이 매개변수로 넘긴 후 필드 값을 수정할 수 있다.

function printOwing(invoice) {
  let outstanding = 0

  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  recordDueDate(invoice)

  // 세부 사항을 출력합니다.
  printDetails(invoice, outstanding)
}

function recordDueDate(invoice) {
  const today = Clock.today
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  )
}

지역 변수의 값을 변경할 때

지역 변수에 값을 대입하게 되면 문제가 복잡해진다. 지금은 변수만을 취급하겠다. 만약 매개변수에 값을 대입하는 코드를 발견하면 곧바로 그 변수를 쪼개서 임시 변수를 새로 하나 만들어 그 변수에 대입하게 한다.

function printOwing(invoice) {
  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  let outstanding = 0 // 맨 위에 있던 선언문을 이 위치로 이동
  for (let i of invoice.orders) {
    outstanding += i.amount
  }

  // 마감일(dueDate)을 기록합니다.
  recordDueDate(invoice)

  // 세부 사항을 출력합니다.
  printDetails(invoice, outstanding)
}

그런 다음 추출할 부분을 새로운 함수로 복사한다.

function printOwing(invoice) {
  printBanner()

  // 미해결 채무(outstanding)를 계산한다.
  let outstanding = calculateOutstanding(invoice)
  // 마감일(dueDate)을 기록합니다.
  recordDueDate(invoice)

  // 세부 사항을 출력합니다.
  printDetails(invoice, outstanding)
}

function calculateOutstanding(invocie){
  let outstanding = 0 // 맨 위에 있던 선언문을 이 위치로 이동
  for (let i of invoice.orders) {
    outstanding += i.amount
  }
  return outstanding
}

outstanding의 선언문을 추출할 코드 앞으로 옮겼기 때문에 매개 변수로 전달하지 않아도 된다. 추출한 코드에서 값이 변경된 변수는 outstanding뿐이다. 따라서 이 값을 반환한다. 167p

다음으로 넘어가서 추출한 코드의 원래 자리를 새로 뽑아낸 함수를 호출하는 문장으로 교체한다.

function printOwing(invoice) {
  printBanner()

  // 함수 추출 완료
  const outstanding = calculateOutstanding(invoice)
  recordDueDate(invoice)
  printDetails(invoice, outstanding)
}

function calculateOutstanding(invocie){
  let outstanding = 0 // 맨 위에 있던 선언문을 이 위치로 이동
  for (let i of invoice.orders) {
    outstanding += i.amount
  }
  return outstanding
}

그 다음 변수명을 변경한다.

function printOwing(invoice) {
  printBanner()

  // 함수 추출 완료
  const outstanding = calculateOutstanding(invoice)
  recordDueDate(invoice)
  printDetails(invoice, outstanding)
}

function calculateOutstanding(invocie){
  let result = 0 // 맨 위에 있던 선언문을 이 위치로 이동
  for (let i of invoice.orders) {
    result += i.amount
  }
  return result
}
728x90

'it 책 > 리팩터링' 카테고리의 다른 글

리팩토링의 원칙  (0) 2022.06.16