[Swift] Có cách nào để tạo tính kế thừa cho Struct?

Mặc dù struct không hỗ trợ kế thừa trực tiếp, nhưng vẫn có các kỹ thuật cho phép chúng ta đạt được nhiều lợi ích tương tự như kế thừa. Protocols và protocol extensions đặc biệt mạnh mẽ trong Swift, cho phép chúng ta tạo ra các abstraction và chia sẻ hành vi mà không cần đến kế thừa truyền thống.

Việc sử dụng các kỹ thuật này không chỉ giúp chúng ta vượt qua giới hạn của struct mà còn thường dẫn đến thiết kế code linh hoạt và dễ bảo trì hơn. Đây là một ví dụ tuyệt vời về cách Swift khuyến khích "composition over inheritance" (ưu tiên kết hợp hơn kế thừa), một nguyên tắc quan trọng trong lập trình hướng đối tượng hiện đại.

1. Sử dụng Protocol và Protocol Extensions

Đây là cách phổ biến nhất để tạo ra sự "kế thừa" cho struct:

protocol Vehicle {
    var wheels: Int { get }
    var color: String { get set }
    func describe()
}

extension Vehicle {
    func describe() {
        print("This vehicle has \(wheels) wheels and is \(color).")
    }
}

struct Car: Vehicle {
    let wheels: Int = 4
    var color: String
    var brand: String
    
    func honk() {
        print("Beep beep!")
    }
}

let myCar = Car(color: "red", brand: "Toyota")
myCar.describe() // This vehicle has 4 wheels and is red.
myCar.honk() // Beep beep!

2. Composition (Kết hợp)

Thay vì kế thừa, chúng ta có thể sử dụng composition:

struct Vehicle {
    var wheels: Int
    var color: String
    
    func describe() {
        print("This vehicle has \(wheels) wheels and is \(color).")
    }
}

struct Car {
    var vehicle: Vehicle
    var brand: String
    
    init(color: String, brand: String) {
        self.vehicle = Vehicle(wheels: 4, color: color)
        self.brand = brand
    }
    
    func describe() {
        vehicle.describe()
        print("It's a \(brand) car.")
    }
}

let myCar = Car(color: "blue", brand: "Honda")
myCar.describe()
// This vehicle has 4 wheels and is blue.
// It's a Honda car.

3. Generics và Associated Types

Sử dụng generics để tạo ra các struct linh hoạt:

protocol VehicleType {
    associatedtype WheelType
    var wheels: [WheelType] { get set }
    var color: String { get set }
}

struct Wheel {
    let size: Int
}

struct Car: VehicleType {
    var wheels: [Wheel]
    var color: String
    var brand: String
    
    init(color: String, brand: String, wheelSize: Int) {
        self.wheels = [Wheel](repeating: Wheel(size: wheelSize), count: 4)
        self.color = color
        self.brand = brand
    }
}

let myCar = Car(color: "green", brand: "Tesla", wheelSize: 18)
print("My \(myCar.brand) car is \(myCar.color) with \(myCar.wheels.count) wheels.")