Kotlin Là Gì?
Kotlin Là Gì?

OOP Mastery: Encapsulation trong Kotlin

OOP Mastery là chuỗi bài viết tự học Lập trình hướng đối tượng với ngôn ngữ Kotlin được biên soạn tại DanTech0xFF. Mục đích của chuỗi bài viết này là cung cấp cái nhìn toàn diện về Hướng đối tượng trong ngôn ngữ Kotlin. Đây sẽ là nền tảng vững chắc cho Lập trình viên trong tương lai nếu các bạn đi sâu về mảng Android, Mobile Cross Platform, hoặc Backend Java.

Trong bài viết này chúng ta sẽ phân tích Tính Đóng gói của OOP trong ngôn ngữ lập trình Kotlin.

Nhắc lại tính đóng gói

Tính đóng gói là khả năng kiểm soát mức độ truy cập của các thuộc tính nằm trong 1 Class. Nhằm đảm bảo sự trọng vẹn dữ liệu, logic của 1 Class để phục vụ cho tính năng của Thư viện, hoặc chương trình.

Access Modifiers trong Kotlin

Kotlin cung cấp nhiều loại access modifier

Access ModifiersGiải thích
publicCó thể được truy cập ở mọi nơi trong source code.
internalChỉ có thể được truy cập trong cùng module (khác package name vẫn có thể truy cập được)
protectedChỉ có thể được truy cập trong phạm vi bên trong Class. Được kế thừa cho Sub Class
privateChỉ có thể được truy cập trong phạm vi bên trong Class. Không kế thừa cho Sub Class
Bảng mô tả các Access modifiers trong Kotlin OOP

Khi khai báo mà không kèm cụ thể Access modifier, Kotlin sẽ xem đó mặc định là 1 public access modifier.

class DefineKotlinAccessModifier {
  val publicModifier: String = "Public Modifier"
  internal val internalModifier: String = "Internal Modifier"
  protected val protectedModifier: String = "Protected Modifier"
  private val privateModifier: String = "Private Modifier"
}

Kotlin cung cấp cho Lập trình viên công cụ Properties Getter, Setter để đa dụng hơn trong thiết kế Encapsulation.

class EncapsulationKotlin {
    var dynamicString: String = "Initialized Value"
        private set // giới hạn phạm vi của hàm set, có thể truy cập dynamicString từ bên ngoài, nhưng việc set phải được thực hiện bên trong Class
    var anotherDynamicString: String = "Another Initialized Value"
        set(value) {
            field = "Another $value"
        } // customize lại logic của hàm set cho anotherDynamicString
}

Cách chọn Access Modifier phù hợp

Nguyên tắc chung trong việc thiết kế Class là phải giới hạn việc tự ý thay đổi property của 1 object. Việc giới hạn này giúp cho logic của Class được tập trung, chỉ có những behavior được yêu cầu mới cần public.

Nguyên tắc 1: Nếu 1 biến không cần thiết được truy cập từ bên ngoài, hãy dùng protected hoặc private

class DatabaseService {
  var dbName: String = ""
  // db logics
}
// Đây là một bad pratice khi thiết kế lớp DatabaseService.
//biến dbName để lưu trữ bị mark là public và ở dạng mutable -> có thể bị thay đổi trong runtime.
// Cách cải thiện là

class DatabaseService(private val dbName: String) {
  // Db logics
}
// Lúc này biến dbName lưu duy nhất 1 giá trị cần thiết để khởi tạo database, và không thay đổi được theo thời gian.
// Từ bên ngoài nhìn vào cũng không thể truy cập đến dbName để kiểm soát giá trị của biến này

Nguyên tắc 2: Nếu 1 biến cần thiết được truy cập từ bên ngoài để đọc giá trị, hãy dung protected hoặc private cho setter

class DatabaseService(dbName: String) {
    var databaseName: String
        private set

    init {
        databaseName = dbName
    }

    // Db logics
}

Nguyên tắc 3: Nếu một biến cần thiết được truy cập và set giá trị từ bên ngoài. Không dùng cách set giá trị trực tiếp cho biến, hãy dùng setter để kiếm soát code được tốt hơn.

class MyWifiController() {
    var currentDeviceCount: Int = 0
        set(value) {
            if (value > maxDeviceCount) {
                println("Device count is too high")
                return
            }
            field = value
        }
    var maxDeviceCount: Int = 10
        set(value) {
            if (currentDeviceCount > value) {
                println("Current device count is greater than max device count")
                return
            }
            field = value
        }
}
// Một ví dụ trong trường hợp Lập trình viên cần thiết buộc phải public setter ra ngoài