Menu
CoddyTech

final và hệ phân cấp

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

final ngăn việc ghi đè thêm. Không thể ghi đè final func trong bất kỳ lớp con nào, và không thể tạo lớp con từ final class:

class Account {
    final func id() -> String { "ACC" }   // không thể ghi đè
}

final class Receipt { }                  // không thể tạo lớp con
// class GiftReceipt: Receipt { }        // lỗi: không thể kế thừa lớp final

Đánh dấu một phương thức là final khi các lớp con không được phép thay đổi phương thức đó, chẳng hạn như phương thức áp dụng một quy tắc. Bạn vẫn có thể ghi đè các phương thức khác để tùy chỉnh chi tiết:

class Checkout {
    final func total(_ prices: [Int]) -> Int {
        let sum = prices.reduce(0, +)
        return sum - discount(sum)
    }
    func discount(_ sum: Int) -> Int { 0 }
}

class SaleCheckout: Checkout {
    override func discount(_ sum: Int) -> Int { sum / 10 }
}

print(Checkout().total([50, 50]))
print(SaleCheckout().total([50, 50]))

Đầu ra:

100
90

Hệ thống phân cấp có thể có nhiều cấp. Chương trình tìm phương thức từ lớp của đối tượng rồi lần lượt đi lên các lớp cha, và chạy phần triển khai đầu tiên tìm thấy:

class Animal {
    func move() -> String { "moves" }
    func eat() -> String { "eats" }
}
class Bird: Animal {
    override func move() -> String { "flies" }
}
class Penguin: Bird {
    override func move() -> String { "swims" }
}

let p = Penguin()
print(p.move())
print(p.eat())

Đầu ra:

swims
eats

Hãy giữ hệ phân cấp nông. Các chuỗi kế thừa sâu khó theo dõi vì hành vi của một đối tượng trải rộng trên nhiều lớp. Khi các kiểu dùng chung hành vi mà không có quan hệ is-a, giao thức (protocol, trong chương tiếp theo) thường là công cụ phù hợp hơn.

// Kế thừa nhiều tầng và dễ gặp lỗi
class A { }
class B: A { }
class C: B { }
class D: C { }      // D kế thừa những gì, từ đâu?
challenge icon

Thử thách

Dễ

Discount có một phương thức final là apply(to:), trả về giá trừ đi amount(for:) nhưng không bao giờ thấp hơn 0, còn amount(for:) trả về 0. Hãy khai báo PercentOff (lưu percent, số tiền giảm = giá nhân với percent / 100) và FixedOff (lưu value, số tiền giảm = value), mỗi lớp có một hàm khởi tạo và ghi đè amount(for:). Bản thân apply(to:) vẫn nằm trong Discount.

Code được cung cấp đọc giá rồi đọc các mức giảm p,10 hoặc f,300, áp dụng từng mức giảm vào giá và in ra 1000 -> 900.

Code của bạn nằm trong Discount.swift, PercentOff.swift và FixedOff.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 đã cho: giữ nguyên
var input: [String] = []
while let line = readLine() { input.append(line) }
let price = Int(input[0])!
for line in input.dropFirst() {
    let p = line.split(separator: ",").map(String.init)
    let d: Discount = p[0] == "p" ? PercentOff(percent: Int(p[1])!) : FixedOff(value: Int(p[1])!)
    print("\(price) -> \(d.apply(to: price))")
}
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