계속해서 Statement() 함수 쪼개기
이전까지 작업한 코드
function statement(invoice, plays) {
let totalAmount = 0;
let volumeCredits = 0;
let result = `청구 내역(고객명: ${invoice.customer})\n`;
const format = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format;
function amountFor(perf, play) {
let thisAmount = 0;
switch (play.type) {
case "tragedy":
thisAmount = 40000;
if (perf.audience > 30) thisAmount += 1000 * (perf.audience - 30);
break;
case "comedy":
thisAmount = 30000;
if (perf.audience > 20) thisAmount += 1000 + 500 * (perf.audience - 20);
thisAmount += 300 * perf.audience;
break;
default:
throw new Error(`알 수 없는 장르: ${play.type}`);
}
return thisAmount;
}
for (let perf of invoice.performances) {
const play = plays[perf.playID];
let thisAmount = amountFor(perf, play);
volumeCredits += Math.max(perf.audience - 30, 0);
if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
result += `${play.name}: ${format(thisAmount / 100)} (${
perf.audience
}석)\n`;
totalAmount += thisAmount;
}
result += `총액: ${format(totalAmount / 100)}\n`;
result += `적립 포인트: ${volumeCredits}점\n`;
return result;
}
적립 포인트 계산 코드 추출
- 지난번 포스팅에서 amountFor 함수를 추출할 때와 마찬가지로 불명확한 변수명을 명확하게 바꾼다.
- 매개변수는 그 목적에 맞는 이름으로 쓴다. 내부적으로만 쓰이고 return 하는 변수명은 result를 쓴다.
function volumeCreditsFor(aPerformance) {
let result = 0;
result += Math.max(aPerformance.audience - 30, 0);
if ("comedy" === playFor(aPerformance).type)
result += Math.floor(aPerformance.audience / 5);
return result;
}
- format은 임시 변수에 함수를 대입한 형태인데 함수를 직접 선언해 사용하도록 바꾸었다.
- format 이라는 이름보다 usd 라는 직관적인 이름으로 바꾸었다.
function usd(aNumber) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format(aNumber / 100);
}
volumeCredits 변수 제거
- 반복문쪼개기 => 변수 값을 누적시키는 부분을 분리한다.
- 문장슬라이드하기 (위치 변경) => 변수 초기화 문장을 변수 값 누적 코드 바로 앞으로 옮긴다.
- 함수 추출하기 => 적립 포인트 계산 부분을 별도 함수로 추출한다.
- 변수 인라인하기 => volumeCredits 변수를 제거한다.
function totalVolumeCredits() {
let volumeCredits = 0;
for (let perf of invoice.performances) {
volumeCredits += volumeCreditsFor(perf);
}
return volumeCredits;
}
totalAmount도 같은 원리로 수정
function totalAmount() {
let result = 0;
for (let perf of invoice.performances) {
result += amountFor(perf);
}
return result;
}
결과물
import INVOICE from "../../invoices.json";
import PLAYS from "../../plays.json";
function statement(invoice, plays) {
let result = `청구 내역(고객명: ${invoice.customer})\n`;
for (let perf of invoice.performances) {
result += `${playFor(perf).name}: ${usd(amountFor(perf))} (${
perf.audience
}석)\n`;
}
result += `총액: ${usd(totalAmount())}\n`;
result += `적립 포인트: ${totalVolumeCredits()}점\n`;
return result;
function usd(aNumber) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format(aNumber / 100);
}
function volumeCreditsFor(aPerformance) {
let result = 0;
result += Math.max(aPerformance.audience - 30, 0);
if ("comedy" === playFor(aPerformance).type)
result += Math.floor(aPerformance.audience / 5);
return result;
}
function totalVolumeCredits() {
let volumeCredits = 0;
for (let perf of invoice.performances) {
volumeCredits += volumeCreditsFor(perf);
}
return volumeCredits;
}
function totalAmount() {
let result = 0;
for (let perf of invoice.performances) {
result += amountFor(perf);
}
return result;
}
function playFor(aPerformance) {
return plays[aPerformance.playID];
}
function amountFor(aPerformance) {
let result = 0;
switch (playFor(aPerformance).type) {
case "tragedy":
result = 40000;
if (aPerformance.audience > 30)
result += 1000 * (aPerformance.audience - 30);
break;
case "comedy":
result = 30000;
if (aPerformance.audience > 20)
result += 1000 + 500 * (aPerformance.audience - 20);
result += 300 * aPerformance.audience;
break;
default:
throw new Error(`알 수 없는 장르: ${playFor(aPerformance).type}`);
}
return result;
}
}
리펙터링 관점에서 성능
- 반복문을 쪼개서 성능이 느려지지 않을까 걱정이 될 수 있다.
- 하지만 하드웨어 성능이 좋고 최신 컴파일러는 똑똑하므로 실제 성능에 미치는 영향은 미미하다.
- 그러므로 코드를 보기쉽게 만드는데 더 집중해야한다.
- 만일 성능에 이슈가 생겼다면 그때 적절한 타협을 통해 이슈를 해결하면 된다.