Site logo
Authors
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Published on
Published on

Tìm hiểu Enum trong Swift

Enum giúp mã của bạn trở nên rõ ràng, dễ đọc và dễ bảo trì hơn bằng cách giới hạn các giá trị có thể có của một biến. Dưới đây, chúng ta sẽ cùng nhau khám phá sâu hơn về Enum trong Swift, từ cơ bản đến nâng cao.

Enum là gì?

Enum (enumeration) trong Swift là một kiểu dữ liệu giá trị (value type) cho phép bạn định nghĩa một tập hợp các giá trị liên quan. Mỗi giá trị trong Enum được gọi là một case. Nó thường được sử dụng để biểu diễn một tập hợp các giá trị cố định, ví dụ như các ngày trong tuần, các hướng, các trạng thái của một đối tượng, v.v.

Tại sao sử dụng Enum?

  • Tăng tính đọc hiểu: Enum giúp code trở nên rõ ràng hơn bằng cách giới hạn các giá trị có thể của một biến.
  • An toàn: Việc sử dụng enum giúp tránh các lỗi do nhập liệu sai giá trị.
  • Switch case: Enum kết hợp rất tốt với câu lệnh switch-case để thực hiện các hành động khác nhau dựa trên giá trị của enum.
  • Associated values: Enum có thể chứa các giá trị liên kết, giúp lưu trữ thêm thông tin cho từng trường hợp.
enum DayOfWeek {
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
    case saturday
    case sunday
}

Khai Báo và Sử Dụng Enum

2.1. Khai Báo Enum Bạn khai báo Enum bằng từ khóa enum theo sau là tên Enum và các case bên trong cặp ngoặc nhọn {}.

enum Direction {
    case north
    case south
    case east
    case west
}

2.2. Sử Dụng Enum Bạn có thể tạo biến hoặc hằng từ Enum và gán một case cụ thể cho nó.

let currentDirection = Direction.north

Raw Values (Giá Trị Nguyên Thủy)

3.1. Enum với Raw Values Enum trong Swift có thể có raw values (giá trị nguyên thủy), cho phép mỗi case có một giá trị cố định thuộc kiểu dữ liệu cụ thể như String, Int, hoặc Double.

enum Planet: Int {
    case mercury = 1
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune
}

Trong ví dụ trên, nếu không chỉ định giá trị nguyên thủy cho các case sau mercury, chúng sẽ tự động tăng dần từ giá trị trước đó. Ví dụ, venus sẽ có giá trị 2, earth3, v.v. 3.2. Truy Xuất Giá Trị Nguyên Thủy Bạn có thể truy xuất giá trị nguyên thủy từ một case bằng cách sử dụng thuộc tính rawValue.

let earthOrder = Planet.earth.rawValue
print("Trái Đất là hành tinh thứ \(earthOrder)") // Kết quả: Trái Đất là hành tinh thứ 3

3.3. Khởi Tạo Enum Từ Raw Value Bạn có thể khởi tạo một Enum từ một raw value bằng cách sử dụng trình khởi tạo init?(rawValue:).

if let planet = Planet(rawValue: 4) {
    print("Hành tinh thứ 4 là \(planet)")
} else {
    print("Không tìm thấy hành tinh với giá trị đó")
}
// Kết quả: Hành tinh thứ 4 là mars

Associated Values (Giá Trị Liên Kết)

4.1. Enum với Associated Values Enum cũng hỗ trợ associated values , cho phép mỗi case chứa một hoặc nhiều giá trị liên kết với nó. Đây là cách để thêm dữ liệu vào các case của Enum.

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

4.2. Sử Dụng Enum với Associated Values

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR Code: \(productCode).")
}
// Kết quả: QR Code: ABCDEFGHIJKLMNOP.

Methods Trong Enum

Bạn có thể thêm các phương thức (methods) vào Enum để thực hiện các hành động liên quan đến các case. 5.1. Thêm Method vào Enum

enum CompassPoint {
    case north
    case south
    case east
    case west
    
    func description() -> String {
        switch self {
        case .north:
            return "North"
        case .south:
            return "South"
        case .east:
            return "East"
        case .west:
            return "West"
        }
    }
}

let direction = CompassPoint.east
print(direction.description()) // Kết quả: East

Conforming to Protocols (Tuân Thủ Giao Thức)

Enum có thể tuân thủ các protocol, cho phép chúng tích hợp sâu hơn vào hệ thống của Swift. 6.1. Enum Tuân Thủ Protocol

protocol Describable {
    func describe() -> String
}

enum Beverage: Describable {
    case coffee
    case tea
    case juice
    
    func describe() -> String {
        switch self {
        case .coffee:
            return "A hot beverage made from coffee beans."
        case .tea:
            return "A hot or cold beverage made by steeping tea leaves."
        case .juice:
            return "A liquid naturally contained in fruit."
        }
    }
}

let drink = Beverage.tea
print(drink.describe()) // Kết quả: A hot or cold beverage made by steeping tea leaves.

Recursive Enums (Enum Đệ Quy)

Swift cho phép Enum đệ quy, nghĩa là một case có thể chứa một giá trị của chính Enum đó. 7.1. Enum Đệ Quy

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

7.2. Tính Toán Giá Trị Enum Đệ Quy

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case .number(let value):
        return value
    case .addition(let left, let right):
        return evaluate(left) + evaluate(right)
    case .multiplication(let left, let right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product)) // Kết quả: (5 + 4) * 2 = 18

Khi Nào Nên Sử Dụng Enum?

Enum là công cụ tuyệt vời khi bạn cần:

  1. Định nghĩa các trạng thái cố định : Ví dụ như trạng thái của một đơn hàng (pending, shipped, delivered).

  2. Làm việc với các lựa chọn có liên quan : Như các lựa chọn trong một biểu mẫu (male, female, other).

  3. Nhóm các giá trị liên quan nhưng khác kiểu dữ liệu : Như trong ví dụ về Barcode hoặc ArithmeticExpression.

So Sánh Enum với Struct và Class

Đặc điểm Enum Struct Class
Kiểu dữ liệu Định nghĩa các giá trị liên quan Định nghĩa các kiểu dữ liệu tùy chỉnh Định nghĩa các kiểu dữ liệu tùy chỉnh
Kiểu truyền Giá trị (Value Type) Giá trị (Value Type) Tham chiếu (Reference Type)
Hỗ trợ kế thừa Không Không
Associated Values Không Không, nhưng có thể sử dụng Composition
Phương thức Có thể chứa Có thể chứa Có thể chứa
Raw Values Có thể có Không Không, nhưng có thể sử dụng Composition hoặc Protocol

Lưu ý : Enum không hỗ trợ kế thừa nhưng có thể tuân thủ các protocol, trong khi Struct và Class có những ưu điểm riêng biệt trong các tình huống khác nhau.

Hạn Chế Của Enum

  • Không hỗ trợ kế thừa : Enum không thể kế thừa từ các Enum khác hoặc từ Struct/Class.
  • Giới hạn trong giá trị liên kết : Mỗi case có thể chứa các giá trị liên kết nhưng không thể chứa các thuộc tính lưu trữ.
  • Không thể mở rộng : Bạn không thể thêm các case mới vào Enum sau khi nó đã được định nghĩa.

Kết Luận

Enum là một trong những tính năng mạnh mẽ và linh hoạt của Swift, giúp bạn quản lý các giá trị liên quan một cách hiệu quả và rõ ràng. Từ việc định nghĩa các trạng thái cố định, làm việc với các lựa chọn có liên quan, đến việc xử lý các biểu thức phức tạp, Enum cung cấp giải pháp tối ưu cho nhiều tình huống trong lập trình. Việc hiểu và sử dụng enum một cách hiệu quả sẽ giúp bạn viết code Swift rõ ràng và dễ bảo trì hơn.

Thực Hành Thêm

Để làm quen sâu hơn với Enum, hãy thử các bài tập sau:

  1. Định nghĩa Enum cho các loại phương tiện giao thông :
enum Transportation {
    case car(String)
    case bicycle
    case train(String, Int) // Tên đường sắt và số toa
}
  1. Sử dụng Enum để quản lý các trạng thái của một ứng dụng :
enum AppState {
    case launched
    case active
    case inactive
    case background
    case terminated
}
  1. Tạo Enum để xử lý các loại lỗi trong ứng dụng :
enum NetworkError: Error {
    case badURL
    case requestFailed
    case unknown
}