「リファクタリング 第2版」Swiftでコーディング その21
リファクタリング 第2版
39-41頁 第1章 型による計算処理の再編成「ポリモーフィズムによる条件記述の置き換え(p.279)」
Swift版 createStatementData.swift
import Foundation
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
}
enum ResultError : Error {
case failure(String)
}
public class PerformanceCalculator {
let aPerformance: Performance
let play: Play
init(aPerformance: Performance, play: Play) {
self.aPerformance = aPerformance
self.play = play
}
func amount() throws -> Int {
throw ResultError.failure("サブクラスの責務")
}
func volumeCredits() -> Int {
return max(self.aPerformance.audience - 30, 0)
}
}
class TragedyCalculator : PerformanceCalculator {
override func amount() -> Int {
var result = 40000
if self.aPerformance.audience > 30 {
result += 1000 * (self.aPerformance.audience - 30)
}
return result
}
}
class ComedyCalculator : PerformanceCalculator {
override func amount() throws -> Int {
var result = 30000
if self.aPerformance.audience > 20 {
result += 10000 + 500 * (self.aPerformance.audience - 20)
}
result += 300 * self.aPerformance.audience
return result
}
override func volumeCredits() -> Int {
return super.volumeCredits() + Int(self.aPerformance.audience / 5)
}
}
func createPerformanceCalculator(aPerformance: Performance, play: Play) throws -> PerformanceCalculator {
switch play.type {
case "tragedy":
return TragedyCalculator(aPerformance: aPerformance, play: play)
case "comedy":
return ComedyCalculator(aPerformance: aPerformance, play: play)
default:
throw ResultError.failure("未知の演劇の種類:\(play.type)")
}
}
func playFor(aPerformance:Performance) -> Play {
return plays[aPerformance.playID]!
}
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 {
return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in
num + perf.volumeCredits
}
}
func totalAmount(data:StatementData) -> Int {
return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in
num + perf.amount
}
}
func enrichPerfoemance(aPerformance:[Performance], plays:Dictionary<String, Play>) -> [PerormanceMapPlay] {
var result: [PerormanceMapPlay] = []
for perf in aPerformance {
do {
let calculator = try createPerformanceCalculator(aPerformance: perf, play: playFor(aPerformance: perf))
var perormanceMapPlay = PerormanceMapPlay(performance: perf, play: calculator.play)
perormanceMapPlay.amount = try calculator.amount()
perormanceMapPlay.volumeCredits = calculator.volumeCredits()
result.append(perormanceMapPlay)
}
catch {
print(error.localizedDescription)
}
}
return result
}
func createStatementData(invoice:Invoice, plays:Dictionary<String, Play>) -> StatementData {
var result = StatementData(customer: invoice.customer, performances: enrichPerfoemance(aPerformance: invoice.performances, plays: plays))
result.totalAmount = totalAmount(data: result)
result.totalVolumeCredits = totalVolumeCredits(data: result)
return result
}