Menu
CoddyTech

Mở rộng giao thức

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 29 trên 57.

Phần mở rộng giao thức cung cấp cách triển khai mặc định cho các phương thức của một giao thức. Mọi kiểu tuân theo giao thức đều có sẵn cách triển khai này, và một kiểu vẫn có thể cung cấp phiên bản riêng:

protocol Greeter {
    var name: String { get }
    func greet() -> String
}

extension Greeter {
    func greet() -> String { "Hello, \(name)!" }
}

struct English: Greeter { let name: String }
struct Pirate: Greeter {
    let name: String
    func greet() -> String { "Ahoy, \(name)!" }
}

print(English(name: "Ada").greet())
print(Pirate(name: "Ada").greet())

Đầu ra:

Hello, Ada!
Ahoy, Ada!

Phần mở rộng cũng có thể thêm các phương thức mới, chỉ dựa trên những yêu cầu. Kiểu dữ liệu tuân thủ chỉ cần viết phần nhỏ bắt buộc và nhận được phần còn lại:

protocol Scored {
    var points: [Int] { get }
}

extension Scored {
    var total: Int { points.reduce(0, +) }
    var best: Int { points.max() ?? 0 }
    func summary() -> String { "total \(total), best \(best)" }
}

struct Player: Scored {
    let points: [Int]
}

print(Player(points: [3, 9, 4]).summary())

Đầu ra:

total 16, best 9

Cách này chia sẻ hành vi giữa struct, class và enum mà không cần kế thừa. Thư viện chuẩn cũng hoạt động theo cách tương tự: mọi Collection đều có isEmpty, first và map nhờ các phần mở rộng giao thức.

extension Collection {
    // isEmpty, first, map, filter, ... đều chỉ cần định nghĩa một lần ở đây
    // và mọi mảng (array), tập hợp (set), từ điển (dictionary) và chuỗi (string) đều có thể dùng chúng
}

Một phần mở rộng có ràng buộc chỉ bổ sung phương thức cho một số kiểu tuân thủ giao thức, được viết bằng where:

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

extension Container where Item == Int {
    var sum: Int { items.reduce(0, +) }
}

struct Box: Container {
    var items: [Int]
}

print(Box(items: [2, 5]).sum)

Đầu ra:

7
challenge icon

Thử thách

Dễ

Khai báo một giao thức Priced yêu cầu var price: Int { get } (tính bằng xu) và var formatted: String { get }. Trong phần mở rộng giao thức, đặt giá trị mặc định cho formatted là $4.50, rồi thêm phương thức isAffordable(budget:) để trả về kết quả cho biết giá có nằm trong ngân sách hay không. Cho các cấu trúc Coffee (giá 450) và Sandwich (có thuộc tính price được lưu trữ) tuân theo giao thức này. Coffee phải thay thế giá trị mặc định của formatted bằng coffee $4.50.

Code được cung cấp đọc ngân sách rồi đọc các dòng coffee hoặc sandwich,650. Với mỗi món, code in ra formatted và yes hoặc no.

Viết code trong các tệp Priced.swift, Coffee.swift và Sandwich.swift. main.swift chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

// Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
var input: [String] = []
while let line = readLine() { input.append(line) }
let budget = Int(input[0])!
for line in input.dropFirst() {
    let item: any Priced = line == "coffee" ? Coffee() : Sandwich(price: Int(line.split(separator: ",")[1])!)
    print("\(item.formatted) \(item.isAffordable(budget: budget) ? "yes" : "no")")
}
quiz iconTự kiểm tra

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

Tự luyện tập: Playground Swift