๋งŒ์•ฝ์— ์—ฐ๊ทน ์ข…๋ฅ˜๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ์ด์ œ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ณ€๊ฒฝํ•  ์‹œ์ ์ด๋‹ค.

๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•ด ๊ณ„์‚ฐ์ฝ”๋“œ ์žฌ๊ตฌ์„ฑํ•˜๊ธฐ

  • ์ง€๊ธˆ๊นŒ์ง€ ๊ตฌ์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ํ•œ๋ฒˆ ๋ณด์ž.
// createStatementData.js
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
 
    function enrichPerformance(aPerformance) { 
        const result = Object.assign({}, aPerformance);
        result.play = playFor(result);
        result.amount = amountFor(result); 
        result.volumeCredits = volumeCreditsFor(result);
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function amountFor(aPerformance) {
        let result = 0;
        switch (aPerformance.play.type) {
            case "tragedy": // ๋น„๊ทน
                result = 40000;
                if (aPerformance.audience > 30) {
                    result += 1000 * (aPerformance.audience - 30);
                }
                break;
            case "comedy": // ํฌ๊ทน
                result = 30000;
                if (aPerformance.audience > 20) {
                    result += 10000 + 500 * (aPerformance.audience - 20);
                }
                result += 300 * aPerformance.audience;
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${aPerformance.play.type}');
        }
        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;
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    func totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
}
// statement.js
 
import createStatementData from './createStatementData.js';
 
function statement(invoice, plays) {
    return renderPlainText(createStatementData(invoice, plays));
}
 
function htmlStatement(invoice, plays) {
    return renderHtml(createStatementData(invoice, plays));
}
 
function renderPlainText(data) {
    let result = "์ฒญ๊ตฌ ๋‚ด์—ญ (๊ณ ๊ฐ๋ช…: ${data.customer})\n";
    for (let perf of data.performances) {
      result += " ${perf.play.name}: ${usd(amountFor(perf))} (${perf.audience}์„)\n";
    }
    result += "์ด์•ก: ${usd(data.totalAmount)}\n";
    result += "์ ๋ฆฝ ํฌ์ธํŠธ: ${data.totalVolumeCredits}์ \n";
    return result;
}
 
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(amountFor(perf))}</td></tr>\n";
    }
    result += "</table>\n";
    result += "<p>์ด์•ก: <em>${usd(data.totalAmount)}</em></p>\n";
    result += "<p>์ ๋ฆฝ ํฌ์ธํŠธ: <em>${data.totalVolumeCredits}</em>์ </p>\n";
    return result;
}
 
function usd(aNumber) {
    return new Intl.NumberFormat("en-US",
        { style: "currency", currency: "USD",
        minimumFractionDigits: 2 }).format(aNumber/100);
}
  • ์ด๋ฒˆ์—๋Š” ์—ฐ๊ทน ์žฅ๋ฅด๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.
  • ์—ฐ๊ทน ์žฅ๋ฅด๋ผ ํ•จ์€ โ€œcomedyโ€์™€ ๊ฐ™์ด ํ‘œ๊ธฐ๋œ๊ฑธ ๋งํ•œ๋‹ค.
  • ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด amountFor() ํ•จ์ˆ˜๋ฅผ ๊ฑด๋“ค๊ณ , ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.
  • switch๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ณ , ํ™•์žฅ์ด ๋ ์ˆ˜๋ก ๊ณจ์นซ๊ฑฐ๋ฆฌ๊ฐ€ ๋  ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ๊ตฌ์กฐ์ ์ธ ์š”์†Œ๋กœ ๋ณด์™„ํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•  ๋ฐฉ๋ฒ•์€ ๋‹คํ˜•์„ฑ์ด๋‹ค.
  • ์กฐ๊ฑด๋ถ€ ๋กœ์ง์„ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ฐ”๊พธ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.
  • ์ƒ์† ๊ณ„์ธต์„ ๊ตฌ์„ฑํ•˜์—ฌ ๊ฐ๊ฐ์˜ ์„œ๋ธŒ ํด๋ž˜์Šค๊ฐ€ ๊ตฌ์ฒด์ ์ธ ๊ณ„์‚ฐ ๋กœ์ง์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์ž.
  • ์•ž์„œ ๋ฆฌํŒฉํ† ๋ง์„ ํ•ด๋‘์—ˆ๊ธฐ ๋•Œ๋ฌธ์— createStatementData.js์—๋งŒ ์ง‘์ค‘ํ•˜๋ฉด ๋œ๋‹ค.
// createStatementData.js
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const result = Object.assign({}, aPerformance);
        result.play = playFor(result);
        result.amount = amountFor(result); 
        result.volumeCredits = volumeCreditsFor(result);
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function amountFor(aPerformance) {
        let result = 0;
        switch (aPerformance.play.type) {
            case "tragedy": // ๋น„๊ทน
                result = 40000;
                if (aPerformance.audience > 30) {
                    result += 1000 * (aPerformance.audience - 30);
                }
                break;
            case "comedy": // ํฌ๊ทน
                result = 30000;
                if (aPerformance.audience > 20) {
                    result += 10000 + 500 * (aPerformance.audience - 20);
                }
                result += 300 * aPerformance.audience;
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${aPerformance.play.type}');
        }
        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;
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    func totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
}

๊ณต์—ฐ๋ฃŒ ๊ณ„์‚ฐ๊ธฐ ๋งŒ๋“ค๊ธฐ

  • ์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, amountFor() ํ•จ์ˆ˜๊ฐ€ aPerformance๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์•ˆ์—์„œ type์— ๋”ฐ๋ผ ๊ณ„์‚ฐ๋กœ์ง์ด ๋ถ„๊ธฐ๋˜๊ณ  ์žˆ๋‹ค.
  • volumeCreditsFor() ํ•จ์ˆ˜๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค.
  • ์œ„์˜ ๋‘ ํ•จ์ˆ˜๋ฅผ ์ „์šฉ ํด๋ž˜์Šค๋กœ ์˜ฎ๊ธฐ์ž. (PerformanceCalculator)
// createStatementData.js
 
class PerformanceCalculator {
    constructor(aPerformance) {
        this.performance = aPerformance;
    }
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = new PerformanceCalculator(aPerformance);
        const result = Object.assign({}, aPerformance);
        result.play = playFor(aPerformance);
        result.amount = amountFor(result); 
        result.volumeCredits = volumeCreditsFor(result);
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function amountFor(aPerformance) {
        let result = 0;
        switch (aPerformance.play.type) {
            case "tragedy": // ๋น„๊ทน
                result = 40000;
                if (aPerformance.audience > 30) {
                    result += 1000 * (aPerformance.audience - 30);
                }
                break;
            case "comedy": // ํฌ๊ทน
                result = 30000;
                if (aPerformance.audience > 20) {
                    result += 10000 + 500 * (aPerformance.audience - 20);
                }
                result += 300 * aPerformance.audience;
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${aPerformance.play.type}');
        }
        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;
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    func totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
}
  • ์ปดํŒŒ์ผ - ํ…Œ์ŠคํŠธ - ์ปค๋ฐ‹ํ•œ๋‹ค.
  • ์ผ๋‹จ ์ด ๊ณ„์‚ฐ๊ธฐ์—์„œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•ด๋ณด์ž.
  • ์—ฐ๊ทน ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ์ •๋ณด๋ฅผ ์ผ๋‹จ ์•ˆ์œผ๋กœ ๋ฐ›์•„๋ณด์ž.
// createStatementData.js
 
class PerformanceCalculator {
    constructor(aPerformance, aPlay) { // ๋ณ€๊ฒฝ๋จ
        this.performance = aPerformance;
        this.play = aPlay; // ๋ณ€๊ฒฝ๋จ
    }
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = new PerformanceCalculator(aPerformance, playFor(aPerformance)); // ๋ณ€๊ฒฝ๋จ
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; // ๋ณ€๊ฒฝ๋จ
        result.amount = amountFor(result); 
        result.volumeCredits = volumeCreditsFor(result);
        return result;
    }
 
    ...
}

ํ•จ์ˆ˜๋“ค์„ ๊ณ„์‚ฐ๊ธฐ๋กœ ์˜ฎ๊ธฐ๊ธฐ

  • ์ด ๋‹ค์Œ์€ ๊ณต์—ฐ๋ฃŒ ๊ณ„์‚ฐ ๋กœ์ง์ด๋‹ค.
  • ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ์ž‘์—…์ด ์ข€ ์ปค์ง„๋‹ค.
  • ์ผ๋‹จ amountFor() ํ•จ์ˆ˜๋ฅผ ์˜ฎ๊ฒจ๋ณด์ž.
  • ์•„์˜ˆ PerfomanaceCalculator ํด๋ž˜์Šค ์•ˆ์—์„œ ์ž‘์—… ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜์ž.
// createStatementData.js
 
class PerformanceCalculator {
    constructor(aPerformance, aPlay) { 
        this.performance = aPerformance;
        this.play = aPlay; 
    }
 
    function amount() {
        let result = 0;
        switch (this.play.type) { // ๋ณ€๊ฒฝ๋จ
            case "tragedy":
                result = 40000;
                if (this.performance.audience > 30) { // ๋ณ€๊ฒฝ๋จ
                    result += 1000 * (this.performance.audience - 30); // ๋ณ€๊ฒฝ๋จ
                }
                break;
            case "comedy":
                result = 30000;
                if (this.performance.audience > 20) { // ๋ณ€๊ฒฝ๋จ 
                    result += 10000 + 500 * (this.performance.audience - 20); // ๋ณ€๊ฒฝ๋จ
                }
                result += 300 * this.performance.audience; // ๋ณ€๊ฒฝ๋จ
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${this.play.type}'); // ๋ณ€๊ฒฝ๋จ
        }
        return result;
    }
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = new PerformanceCalculator(aPerformance, playFor(aPerformance)); 
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; 
        result.amount = calculator.amount(); // ๋ณ€๊ฒฝ๋จ
        result.volumeCredits = volumeCreditsFor(result);
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    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 totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    func totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
    ...
}
  • amountFor() ํ•จ์ˆ˜๋ฅผ PerformanceCalculator ํด๋ž˜์Šค๋กœ ์˜ฎ๊ฒผ๋‹ค.
  • ๋‹ค์Œ์€ ์ ๋ฆฝ ํฌ์ธํŠธ ๋ถ€๋ถ„์ด๋‹ค.
// createStatementData.js
 
class PerformanceCalculator {
    constructor(aPerformance, aPlay) { 
        this.performance = aPerformance;
        this.play = aPlay; 
    }
 
    get amount() {
        let result = 0;
        switch (this.play.type) {
            case "tragedy":
                result = 40000;
                if (this.performance.audience > 30) {
                    result += 1000 * (this.performance.audience - 30);
                }
                break;
            case "comedy":
                result = 30000;
                if (this.performance.audience > 20) { 
                    result += 10000 + 500 * (this.performance.audience - 20);
                }
                result += 300 * this.performance.audience;
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${this.play.type}');
        }
        return result;
    }
 
    get volumeCredits() {
        let result = 0;
        result += Math.max(this.aPerformance.audience - 30, 0); // ๋ณ€๊ฒฝ๋จ
        if ("comedy" === this.play.type) // ๋ณ€๊ฒฝ๋จ
            result += Math.floor(this.aPerformance.audience / 5); // ๋ณ€๊ฒฝ๋จ
        return result;
    }
 
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = new PerformanceCalculator(aPerformance, playFor(aPerformance)); 
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; 
        result.amount = calculator.amount();
        result.volumeCredits = calculator.volumeCredits(); // ๋ณ€๊ฒฝ๋จ
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    function totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
    ...
}

๊ณต์—ฐ๋ฃŒ ๊ณ„์‚ฐ๊ธฐ๋ฅผ ๋‹คํ˜•์„ฑ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค๊ธฐ

  • ์ด์ œ PerformanceCalculator ํด๋ž˜์Šค๋ฅผ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ฐ”๊ฟ”๋ณด์ž.
  • ๊ฐ€์žฅ ๋จผ์ €ํ•  ์ผ์€ .type ๋Œ€์‹  ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด๋‹ค. (ํƒ€์ž… ์ฝ”๋“œ๋ฅผ ์„œ๋ธŒํด๋ž˜์Šค๋กœ ๋ฐ”๊พธ๊ธฐ)
  • ์ผ๋‹จ ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์ž.
// createStatementData.js
 
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}');
    }
}
 
class TragedyCalculator(aPerformance, aPlay) {
    
}
 
class ComedyCalculator(aPerformance, aPlay) {
    
}
 
class PerformanceCalculator {
    constructor(aPerformance, aPlay) { 
        this.performance = aPerformance;
        this.play = aPlay; 
    }
 
    get amount() {
        let result = 0;
        switch (this.play.type) {
            case "tragedy":
                result = 40000;
                if (this.performance.audience > 30) {
                    result += 1000 * (this.performance.audience - 30);
                }
                break;
            case "comedy":
                result = 30000;
                if (this.performance.audience > 20) { 
                    result += 10000 + 500 * (this.performance.audience - 20);
                }
                result += 300 * this.performance.audience;
                break;
            default:
                throw new Error('์•Œ ์ˆ˜ ์—†๋Š” ์žฅ๋ฅด: ${this.play.type}');
        }
        return result;
    }
 
    get volumeCredits() {
        let result = 0;
        result += Math.max(this.aPerformance.audience - 30, 0); // ๋ณ€๊ฒฝ๋จ
        if ("comedy" === this.play.type) // ๋ณ€๊ฒฝ๋จ
            result += Math.floor(this.aPerformance.audience / 5); // ๋ณ€๊ฒฝ๋จ
        return result;
    }
 
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = createPerformanceCalculator(aPerformance, playFor(aPerformance)); 
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; 
        result.amount = calculator.amount();
        result.volumeCredits = calculator.volumeCredits(); // ๋ณ€๊ฒฝ๋จ
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    function totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
    ...
}
 
  • ์ปดํŒŒ์ผ - ํ…Œ์ŠคํŠธ - ์ปค๋ฐ‹ํ•œ๋‹ค.
  • ์ด์ œ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ฐ”๊ฟ”๋ณด์ž.
// createStatementData.js
 
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}');
    }
}
 
class TragedyCalculator(aPerformance, aPlay) {
 
    get amount() { // ๋ณ€๊ฒฝ๋จ
        let result = 40000;
        if (this.performance.audience > 30) {
            result += 1000 * (this.performance.audience - 30);
        }
        return result;
    }
    
}
 
class ComedyCalculator(aPerformance, aPlay) {
 
    get amount() { // ๋ณ€๊ฒฝ๋จ
        let result = 30000;
        if (this.performance.audience > 20) { 
            result += 10000 + 500 * (this.performance.audience - 20);
        }
        result += 300 * this.performance.audience;
        return result;
    }
 
}
 
class PerformanceCalculator {
 
    constructor(aPerformance, aPlay) { 
        this.performance = aPerformance;
        this.play = aPlay; 
    }
 
    get amount() {
        throw new Error('์„œ๋ธŒํด๋ž˜์Šค์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); // ๋ณ€๊ฒฝ๋จ
    }
 
    get volumeCredits() {
        let result = 0;
        result += Math.max(this.aPerformance.audience - 30, 0);
        if ("comedy" === this.play.type)
            result += Math.floor(this.aPerformance.audience / 5);
        return result;
    }
 
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = createPerformanceCalculator(aPerformance, playFor(aPerformance)); 
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; 
        result.amount = calculator.amount();
        result.volumeCredits = calculator.volumeCredits(); // ๋ณ€๊ฒฝ๋จ
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    function totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
    ...
}
  • ์ปดํŒŒ์ผ - ํ…Œ์ŠคํŠธ - ์ปค๋ฐ‹ํ•œ๋‹ค.
  • ๋‹ค์Œ์œผ๋กœ๋Š” volumeCredits() ํ•จ์ˆ˜๋ฅผ ์˜ฎ๊ฒจ๋ณด์ž.
// createStatementData.js
 
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}');
    }
}
 
class TragedyCalculator(aPerformance, aPlay) {
 
    get amount() {
        let result = 40000;
        if (this.performance.audience > 30) {
            result += 1000 * (this.performance.audience - 30);
        }
        return result;
    }
    
}
 
class ComedyCalculator(aPerformance, aPlay) {
 
    get amount() {
        let result = 30000;
        if (this.performance.audience > 20) { 
            result += 10000 + 500 * (this.performance.audience - 20);
        }
        result += 300 * this.performance.audience;
        return result;
    }
 
    get volumeCredits() {  // ๋ณ€๊ฒฝ๋จ
        super.volumeCredits + Math.floor(this.aPerformance.audience / 5);
    }
 
}
 
class PerformanceCalculator {
 
    constructor(aPerformance, aPlay) { 
        this.performance = aPerformance;
        this.play = aPlay; 
    }
 
    get amount() {
        throw new Error('์„œ๋ธŒํด๋ž˜์Šค์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
    }
 
    get volumeCredits() {
        Math.max(this.aPerformance.audience - 30, 0);
    }
 
}
 
export default function createStatementData(invoice, plays) {
    const statementData = {};
    statementData.customer = invoice.customer;
    statementData.performances = invoice.performances.map(enrichPerformance);
    statementData.totalAmount = totalAmount();
    statementData.totalVolumeCredits = totalVolumeCredits();
    return statementData;
 
    function enrichPerformance(aPerformance) { 
        const calculator = createPerformanceCalculator(aPerformance, playFor(aPerformance)); 
        const result = Object.assign({}, aPerformance);
        result.play = calculator.play; 
        result.amount = calculator.amount();
        result.volumeCredits = calculator.volumeCredits();
        return result;
    }
 
    function playFor(aPerformance) {
        return plays[aPerformance.playID];
    }
 
    function totalAmount(data) {
        return data.performances
            .reduce((total, p) => total + p.amount, 0);
    }
 
    function totalVolumeCredits(data) {
        data.performances
            .reduce((total, p) => total + p.volumeCredits, 0); 
    }
    ...
}

Reference