2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

「リファクタリング 第2版」Swiftでコーディング 第1章完了

Posted at

第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)
    }
}
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?