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.
- Part 1: OOP Mastery: Các loại Class, Interface trong Kotlin
- Part 2: OOP Mastery: Encapsulation trong Kotlin
- Part 3: OOP Mastery: Inheritance trong Kotlin
- Part 4: OOP Mastery: Tính đa hình – Polymorphism trong Kotlin
- Part 5: OOP Mastery: Tính Trừu tượng – Abstraction trong Kotlin
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 Modifiers | Giải thích |
public | Có thể được truy cập ở mọi nơi trong source code. |
internal | Chỉ có thể được truy cập trong cùng module (khác package name vẫn có thể truy cập được) |
protected | Chỉ có thể được truy cập trong phạm vi bên trong Class. Được kế thừa cho Sub Class |
private | Chỉ có thể được truy cập trong phạm vi bên trong Class. Không kế thừa cho Sub Class |
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