1.5 중첩이 난무하는 함수
1.6 계산 단계와 포맷팅 단계 분리하기
1.7 중간 점검: 두 파일(과 두 단계)로 분리됨
class PerformanceCalculator {
constructor(aPerformance, aPlay) {
this.performance = aPerformance;
this.play = aPlay;
}
get amount() {
throw new Error("서브클래스에서 처리하도록 설계");
}
get volumeCredits() {
throw new Error("서브클래스에서 처리하도록 설계");
}
}
class TragedyCalculator extends PerformanceCalculator {
get amount() {
let result = 40000;
if (this.performance.audience > 30)
result += 1000 * (this.performance.audience - 30);
return result;
}
get volumeCredits() {
return Math.max(this.performance.audience - 30, 0);
}
}
class ComedyCalculator extends PerformanceCalculator {
get amount() {
let result = 30000;
if (this.performance.audience > 20)
result += 1000 + 500 * (this.performance.audience - 20);
result += 300 * this.performance.audience;
return result;
}
get volumeCredits() {
return Math.floor(this.performance.audience / 5);
}
}
export default function createStatementData(invoice, plays) {
const statementData = {};
statementData.customer = invoice.customer;
statementData.performances = invoice.performances.map(enrichPerformance);
statementData.totalAmount = totalAmount(statementData);
statementData.totalVolumeCredits = totalVolumeCredits(statementData);
return statementData;
function enrichPerformance(aPerformance) {
const calculator = createPerformanceCalculator(
aPerformance,
playFor(aPerformance)
);
const result = Object.assign({}, aPerformance);
result.play = playFor(result);
result.amount = calculator.amount;
result.volumeCredits = calculator.volumeCredits;
return result;
}
function createPerformanceCalculator(aPerformance, aPlay) {
switch (aPlay.type) {
case "tragedy":
return new TragedyCalculator(aPerformance, aPlay);
case "comedy":
return new ComedyCalculator(aPerformance, aPlay);
default:
throw new Error(`알 수 없는 장르: ${aPlay.type}`);
}
}
function playFor(aPerformance) {
return plays[aPerformance.playID];
}
function totalVolumeCredits(data) {
return data.performances.reduce((total, p) => total + p.volumeCredits, 0);
}
function totalAmount(data) {
return data.performances.reduce((total, p) => total + p.amount, 0);
}
}
1.8 다형성을 활용해 계산 코드 재구성하기
1.9 상태 점검: 다형성을 활용하여 데이터 생성하기
import INVOICE from "../../invoices.json";
import PLAYS from "../../plays.json";
import createStatementData from "./createStatementData.js";
function statement(invoice, plays) {
return renderPlainText(createStatementData(invoice, plays));
}
function renderPlainText(data, plays) {
let result = `청구 내역(고객명: ${data.customer})\n`;
for (let perf of data.performances) {
result += `${perf.play.name}: ${usd(perf.amount)} (${perf.audience}석)\n`;
}
result += `총액: ${usd(data.totalAmount)}\n`;
result += `적립 포인트: ${data.totalVolumeCredits}점\n`;
return result;
}
function htmlStatement(invoice, plays) {
return renderHtml(createStatementData(invoice, plays));
}
function renderHtml(data) {
let result = `<h1>청구 내역(고객명: ${data.customer})</h1>\n`;
result += `<table>\n`;
result += `<tr><th>연극</th><th>좌석 수</th><th>금액</th></tr>`;
for (let perf of data.performances) {
result += `<tr><td>${perf.play.name}</td><td>(${perf.audience}석)</td>`;
result += `<td>${usd(perf.amount)}</td></tr>\n`;
}
result += `</table>\n`;
result += `총액: ${usd(data.totalAmount)}\n`;
result += `적립 포인트: ${data.totalVolumeCredits}점\n`;
return result;
}
function usd(aNumber) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format(aNumber / 100);
}
document.getElementById("app2").innerText = statement(INVOICE[0], PLAYS);
document.getElementById("app").innerHTML = htmlStatement(INVOICE[0], PLAYS);
class PerformanceCalculator {
constructor(aPerformance, aPlay) {
this.performance = aPerformance;
this.play = aPlay;
}
get amount() {
throw new Error("서브클래스에서 처리하도록 설계");
}
get volumeCredits() {
throw new Error("서브클래스에서 처리하도록 설계");
}
}
class TragedyCalculator extends PerformanceCalculator {
get amount() {
let result = 40000;
if (this.performance.audience > 30)
result += 1000 * (this.performance.audience - 30);
return result;
}
get volumeCredits() {
return Math.max(this.performance.audience - 30, 0);
}
}
class ComedyCalculator extends PerformanceCalculator {
get amount() {
let result = 30000;
if (this.performance.audience > 20)
result += 1000 + 500 * (this.performance.audience - 20);
result += 300 * this.performance.audience;
return result;
}
get volumeCredits() {
return Math.floor(this.performance.audience / 5);
}
}
export default function createStatementData(invoice, plays) {
const statementData = {};
statementData.customer = invoice.customer;
statementData.performances = invoice.performances.map(enrichPerformance);
statementData.totalAmount = totalAmount(statementData);
statementData.totalVolumeCredits = totalVolumeCredits(statementData);
return statementData;
function enrichPerformance(aPerformance) {
const calculator = createPerformanceCalculator(
aPerformance,
playFor(aPerformance)
);
const result = Object.assign({}, aPerformance);
result.play = playFor(result);
result.amount = calculator.amount;
result.volumeCredits = calculator.volumeCredits;
return result;
}
function createPerformanceCalculator(aPerformance, aPlay) {
switch (aPlay.type) {
case "tragedy":
return new TragedyCalculator(aPerformance, aPlay);
case "comedy":
return new ComedyCalculator(aPerformance, aPlay);
default:
throw new Error(`알 수 없는 장르: ${aPlay.type}`);
}
}
function playFor(aPerformance) {
return plays[aPerformance.playID];
}
function totalVolumeCredits(data) {
return data.performances.reduce((total, p) => total + p.volumeCredits, 0);
}
function totalAmount(data) {
return data.performances.reduce((total, p) => total + p.amount, 0);
}
}
import INVOICE from "../../invoices.json";
import PLAYS from "../../plays.json";
import createStatementData from "./createStatementData.js";
function statement(invoice, plays) {
return renderPlainText(createStatementData(invoice, plays));
}
function renderPlainText(data, plays) {
let result = `청구 내역(고객명: ${data.customer})\n`;
for (let perf of data.performances) {
result += `${perf.play.name}: ${usd(perf.amount)} (${perf.audience}석)\n`;
}
result += `총액: ${usd(data.totalAmount)}\n`;
result += `적립 포인트: ${data.totalVolumeCredits}점\n`;
return result;
}
function htmlStatement(invoice, plays) {
return renderHtml(createStatementData(invoice, plays));
}
function renderHtml(data) {
let result = `<h1>청구 내역(고객명: ${data.customer})</h1>\n`;
result += `<table>\n`;
result += `<tr><th>연극</th><th>좌석 수</th><th>금액</th></tr>`;
for (let perf of data.performances) {
result += `<tr><td>${perf.play.name}</td><td>(${perf.audience}석)</td>`;
result += `<td>${usd(perf.amount)}</td></tr>\n`;
}
result += `</table>\n`;
result += `총액: ${usd(data.totalAmount)}\n`;
result += `적립 포인트: ${data.totalVolumeCredits}점\n`;
return result;
}
function usd(aNumber) {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2
}).format(aNumber / 100);
}
document.getElementById("app2").innerText = statement(INVOICE[0], PLAYS);
document.getElementById("app").innerHTML = htmlStatement(INVOICE[0], PLAYS);