「リファクタリング 第2版」Swiftでコーディング その17

# 25-29頁 第1章 計算とフォーマットにフェーズ分割「フェーズの分離(p.160)」「関数の移動(p.206)」

JavaScriptとSwiftの差を埋めています。
Struct「StatementData」「PerormanceMapPlay」を新規作成しています。

## Swift版 main.swift

データ生成、結果表示付き。

```swift
import Foundation

makeData()

struct PerormanceMapPlay {
    let performance: Performance
    let play: Play
    var amount: Int = 0
    var volumeCredits: Int = 0
}

struct StatementData {
    let customer: String
    let performances: [PerormanceMapPlay]
    var totalAmount: Int = 0
    var totalVolumeCredits: Int = 0
}

func playFor(aPerformance:Performance) -> Play {
    return plays[aPerformance.playID]!
}

func volumeCreditsFor(aPerformance:Performance) -> Int {
    var result = 0
    result += max(aPerformance.audience - 30, 0)
    if "comedy" == playFor(aPerformance: aPerformance).type {
        result += Int(aPerformance.audience / 5)
    }
    return result
}

func usd(aNumber:Int) -> String {
    let format = NumberFormatter()
    format.numberStyle = .currency
    format.locale = Locale(identifier: "en_US")
    return format.string(from: NSNumber(value: aNumber / 100))!
}

func totalVolumeCredits(data:StatementData) -> Int {
    var result = 0
    for perf in data.performances {
        result += perf.volumeCredits
    }
    return result
}

func totalAmount(data:StatementData) -> Int {
    var result = 0
    for perf in data.performances {
        result += perf.amount
    }
    return result
}

func amountFor(aPerformance:PerormanceMapPlay) -> Int {
    var result = 0

    switch aPerformance.play.type {
    case "tragedy":
        result = 40000
        if aPerformance.performance.audience > 30 {
            result += 1000 * (aPerformance.performance.audience - 30)
        }
    case "comedy":
        result = 30000
        if aPerformance.performance.audience > 20 {
            result += 10000 + 500 * (aPerformance.performance.audience - 20)
        }
        result += 300 * aPerformance.performance.audience
    default:
        print("error")
    }
    return result
}

func enrichPerfoemance(aPerformance:[Performance], plays:Dictionary<String, Play>) -> [PerormanceMapPlay] {
    var result: [PerormanceMapPlay] = []
    for perf in aPerformance {
        var perormanceMapPlay = PerormanceMapPlay(performance: perf, play: playFor(aPerformance: perf))
        perormanceMapPlay.amount = amountFor(aPerformance: perormanceMapPlay)
        perormanceMapPlay.volumeCredits = volumeCreditsFor(aPerformance: perf)
        result.append(perormanceMapPlay)
    }
    return result
}

func statement(invoice:Invoice, plays:Dictionary<String, Play>) -> String {
    var statementData = StatementData(customer: invoice.customer, performances: enrichPerfoemance(aPerformance: invoice.performances, plays: plays))
    statementData.totalAmount = totalAmount(data: statementData)
    statementData.totalVolumeCredits = totalVolumeCredits(data: statementData)
    return renderPlainText(data: statementData, plays: plays)
}

func renderPlainText(data:StatementData, plays:Dictionary<String, Play>) -> String {
    var result = "Statement for \(data.customer)\n"

    for perf in data.performances {
        result += "  \(perf.play.name): " + usd(aNumber: perf.amount) + " (\(perf.performance.audience) seats)\n"
    }
    result += "Amount owed is " + usd(aNumber: data.totalAmount) + "\n"
    result += "You earned \(data.totalVolumeCredits) credits\n"
    return result
}

let result = statement(invoice: invoices[0], plays: plays)
print(result)
```