Bảo vệ trạng thái
Thuộc phần Lập trình hướng đối tượng trong hành trình Swift của Coddy. Bài học 19 trên 57.
Tính bất biến là quy tắc luôn phải đúng với một giá trị, chẳng hạn như “số dư không bao giờ âm”. Kiểu dữ liệu bảo vệ các tính bất biến bằng cách kiểm tra mọi thay đổi trước khi áp dụng:
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)Đầu ra:
false
true
60Khi bên gọi cần biết lý do một thay đổi bị từ chối, phương thức có thể ném ra một lỗi thay vì trả về false. Kiểu lỗi là một enum tuân theo 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")
}Đầu ra:
missing 20Đặt mọi thay đổi đối với dữ liệu được bảo vệ bên trong các phương thức kiểm tra dữ liệu đó. Nếu có thể thiết lập thuộc tính từ bên ngoài, bất kỳ dòng code nào cũng có thể phá vỡ quy tắc mà kiểu dữ liệu không nhận ra:
struct Temperature {
private(set) var kelvin: Double // phải luôn >= 0
mutating func cool(by amount: Double) {
kelvin = max(0, kelvin - amount) // cách duy nhất để giảm giá trị này
}
}Một thao tác thay đổi nhiều thuộc tính phải giữ chúng nhất quán ngay cả khi thất bại giữa chừng. Trước tiên, hãy kiểm tra mọi thứ, sau đó mới thay đổi trạng thái:
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"]!)Đầu ra:
false
2
true
1Thử thách
DễHoàn thiện struct Seats, dùng để bán vé cho một buổi biểu diễn có capacity chỗ ngồi. sold có thuộc tính private(set). buy(_:) ném TicketError.invalidCount khi số lượng nhỏ hơn 1, TicketError.soldOut khi không còn chỗ, và TicketError.notEnough(left:) khi số chỗ còn lại ít hơn số lượng được yêu cầu; nếu không, hàm cộng số vé vào sold.
Code được cung cấp sẽ đọc sức chứa và một số lượng được yêu cầu trên mỗi dòng. Với mỗi dòng, code sẽ in ra sold 3, left 7, invalid count, sold out hoặc only 2 left.
Code của bạn nằm trong TicketError.swift và Seats.swift. main.swift chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.
Tự làm thử
// Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên
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")
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpPhương thức và selfHàm khởi tạoLớp là tham chiếuÔn tập: Thẻ thư viện4Đóng gói
Kiểm soát quyền truy cậpChỉ đọc từ bên ngoàiBảo vệ trạng tháiHàm khởi tạo có thể thất bạiÔn tập: Tài khoản ngân hàng2Struct và kiểu giá trị
StructNgữ nghĩa giá trịPhương thức thay đổiStruct hay classÔn tập: mặt hàng trong kho5Kế thừa
Lớp conGhi đè phương thứcHàm khởi tạo và superfinal và hệ phân cấpÔn tập: hệ phân cấp nhân viên8Các giao thức tiêu chuẩn
CustomStringConvertibleEquatableComparableHashableÔn tập: Sắp xếp tùy chỉnhTự luyện tập: Playground Swift