Menu
Coddy logo textTech

상태 보호

Coddy Swift 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 19번째.

불변식은 "잔액은 절대 음수가 아니다"와 같이 값에 대해 항상 성립해야 하는 규칙입니다. 타입은 적용하기 전에 모든 변경 사항을 확인하여 불변식을 보호합니다:

struct BankAccount {
    private(set) var balance = 0

    mutating func withdraw(_ amount: Int) -> Bool {
        guard amount > 0, amount <= balance else { return false }
        balance -= amount
        return true
    }

    mutating func deposit(_ amount: Int) {
        guard amount > 0 else { return }
        balance += amount
    }
}

var acc = BankAccount()
acc.deposit(100)
print(acc.withdraw(150))
print(acc.withdraw(40))
print(acc.balance)

출력:

false
true
60

호출자가 변경이 거부된 이유를 알아야 하는 경우, 메서드는 false를 반환하는 대신 오류를 발생시킬 수 있습니다. 오류 유형은 Error를 준수하는 열거형입니다.

enum TransferError: Error {
    case invalidAmount
    case insufficientFunds(missing: Int)
}

struct Account {
    private(set) var balance: Int

    mutating func withdraw(_ amount: Int) throws {
        guard amount > 0 else { throw TransferError.invalidAmount }
        guard amount <= balance else { throw TransferError.insufficientFunds(missing: amount - balance) }
        balance -= amount
    }
}

var acc = Account(balance: 30)
do {
    try acc.withdraw(50)
} catch TransferError.insufficientFunds(let missing) {
    print("missing \(missing)")
} catch {
    print("other error")
}

출력:

missing 20

보호된 데이터에 대한 모든 변경은 이를 확인하는 메서드 내부에서 이루어지도록 하세요. 속성을 외부에서 설정할 수 있다면, 어떤 코드 줄이든 규칙을 위반할 수 있고 타입은 이를 알아차리지 못합니다:

struct Temperature {
    private(set) var kelvin: Double   // 0 이상으로 유지되어야 함
    mutating func cool(by amount: Double) {
        kelvin = max(0, kelvin - amount)   // 이를 낮추는 유일한 방법
    }
}

여러 속성을 변경하는 작업은 중간에 실패하더라도 속성 간의 일관성을 유지해야 합니다. 먼저 모든 것을 확인한 다음 상태를 변경하세요:

struct Inventory {
    private(set) var stock: [String: Int] = ["tea": 2, "jam": 1]

    mutating func sell(_ items: [String]) -> Bool {
        var needed: [String: Int] = [:]
        for i in items { needed[i, default: 0] += 1 }
        for (item, n) in needed where (stock[item] ?? 0) < n { return false }
        for (item, n) in needed { stock[item]! -= n }
        return true
    }
}

var inv = Inventory()
print(inv.sell(["tea", "jam", "jam"]))
print(inv.stock["tea"]!)
print(inv.sell(["tea", "jam"]))
print(inv.stock["tea"]!)

출력:

false
2
true
1
challenge icon

챌린지

쉬움

capacity개의 좌석이 있는 공연의 티켓을 판매하는 Seats 구조체를 완성하세요. sold는 private(set)입니다. buy(_:)는 수량이 1보다 작으면 TicketError.invalidCount를, 남은 좌석이 없으면 TicketError.soldOut를, 요청한 수량보다 남은 좌석이 적으면 TicketError.notEnough(left:)를 발생시킵니다. 그 외의 경우에는 티켓을 sold에 추가합니다.

제공된 코드는 수용 인원과 줄마다 하나의 요청 수량을 읽습니다. 각 요청에 대해 sold 3, left 7, invalid count, sold out 또는 only 2 left를 출력합니다.

코드는 TicketError.swift와 Seats.swift에 작성하세요. main.swift에는 제공된 입력/출력 코드가 포함되어 있으며 편집할 수 없습니다.

직접 해보기

// 제공된 입력/출력 코드: 그대로 유지하세요
var input: [String] = []
while let line = readLine() { input.append(line) }
var seats = Seats(capacity: Int(input[0])!)
for line in input.dropFirst() {
    let n = Int(line)!
    do {
        try seats.buy(n)
        print("sold \(n), left \(seats.capacity - seats.sold)")
    } catch TicketError.invalidCount {
        print("invalid count")
    } catch TicketError.soldOut {
        print("sold out")
    } catch TicketError.notEnough(let left) {
        print("only \(left) left")
    } catch {
        print("unexpected")
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: Swift 플레이그라운드