「リファクタリング 第2版」Swiftでコーディング 第1章完了
リファクタリング 第2版
第1章完了
全ソース掲載。
Swiftとしてリファクタリングした方がいい箇所があるかは見直し。
例えば「Result使用」「ファイル分割」
Swift版 main.swift
import Foundation makeData() func statement(invoice:Invoice, plays:Dictionary<String, Play>) -> String { return renderPlainText(data: createStatementData(invoice: invoice, plays: plays)) } func renderPlainText(data:StatementData) -> 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 } func htmlStatement(invoice:Invoice, plays:Dictionary<String, Play>) -> String { renderHtml(data: createStatementData(invoice: invoice, plays: plays)) } func renderHtml(data:StatementData) -> String { var result = "<h1>Statement for \(data.customer)</h1>\n" result += "<table>¥n" result += "<tr><th>play</th><th>seats</th><th>cost</th></tr>\n" for perf in data.performances { result += " <tr><td>\(perf.play.name)</td><td>\(perf.performance.audience)</td>" result += "<td></td>" + usd(aNumber: perf.amount) + "</td></tr>\n" } result += "</table>\n" result += "<p>Amount owed is <em>" + usd(aNumber: data.totalAmount) + "</em></p>\n" result += "<p>You earned <em>\(data.totalVolumeCredits)</em> credits</p>\n" return result } let resultPlain = statement(invoice: invoices[0], plays: plays) print("--- Plain Text ---") print(resultPlain) let resultHtml = htmlStatement(invoice: invoices[0], plays: plays) print("--- HTML ---") print(resultHtml)
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 }
Swift版 data.swift
import Foundation let json_plays = """ { "hamlet": {"name": "Hamlet", "type": "tragedy"}, "aslike": {"name": "As You Like It", "type": "comedy"}, "othello": {"name": "Othello", "type": "tragedy"} } """ let json_invoices = """ [ { "customer": "BigCo", "performances": [ { "playID": "hamlet", "audience": 55 }, { "playID": "aslike", "audience": 35 }, { "playID": "othello", "audience": 40 } ] } ] """ struct Play: Codable { let name: String let type: String } struct Plays: Codable { let hamlet: Play let aslike: Play let othello: Play } struct Performance: Codable { let playID: String let audience: Int } struct Invoice: Codable { let customer: String let performances:[Performance] } let json_plays_data: Data = json_plays.data(using: String.Encoding.utf8)! let json_invoices_data:Data = json_invoices.data(using: String.Encoding.utf8)! var plays:Dictionary<String, Play> = [:] var invoices:[Invoice] = [] func makeData() { let decoder: JSONDecoder = JSONDecoder() do { let json: Plays = try decoder.decode(Plays.self, from: json_plays_data) plays["hamlet"] = json.hamlet plays["aslike"] = json.aslike plays["othello"] = json.othello } catch { print("error:", error.localizedDescription) } do { let json = try decoder.decode([Invoice].self, from: json_invoices_data) invoices = json } catch { print("error:", error.localizedDescription) } }