Singleton Pattern - Bài Học Vỡ Lòng
Singleton Pattern - Bài Học Vỡ Lòng

Học Design Pattern: Singleton Pattern – Ngôi Sao Đô Con

Singleton Pattern không phải là tên 1 dòng đồ uống. Nó là một mẫu thiết kế mà bất kì anh em dev nào cũng từng nghe qua, trải nghiệm qua, dùng suốt luôn, nhưng vẫn không hiểu tại sao nó lại là 1 Design Pattern.

Đặt Vấn Đề – Tại Sao Cần Singleton?

Trong một số logic cụ thể, chúng ta cần đảm bảo rằng một lớp chỉ có duy nhất một Instance và cung cấp một điểm truy cập toàn cục đến Instance đó.

Hãy tưởng tượng bạn đang xây dựng một ứng dụng quản lý chi tiêu. Ứng dụng này cần truy cập đến một đối tượng duy nhất để đọc và ghi (ExpendManager) các chi tiêu của khách hàng. Nếu có nhiều Instance của object ExpendManager, có thể xảy ra xung đột và dữ liệu cấu hình bị sai lệch.

Thách thức ở đây là làm sao để hạn chế việc tạo ra nhiều Instance của lớp ExpendManager và cung cấp một cách thức truy cập thống nhất đến Instance duy nhất đó.

Code Không Dùng Singleton Pattern

class ExpendManager(
    private var databaseUrl: String = "localhost",
    private var timeout: Int = 10
) {

    fun loadExpend() {
        println("Loading expend from DB")
    }

    fun addExpend(name: String, amount: Double) {
        println("Saving expend to DB")
    }
}

fun main() {
    val manager1 = ExpendManager()
    val manager2 = ExpendManager()
}

Ở đây, chúng ta có thể tạo ra nhiều đối tượng ExpendManager, dẫn đến việc các thay đổi trên một đối tượng không được phản ánh trên các đối tượng khác.

Code Dùng Singleton Pattern

class ExpendManager(
    private var databaseUrl: String = "localhost",
    private var timeout: Int = 10
) {

    fun loadExpend() {
        println("Loading expend from DB")
    }

    fun addExpend(name: String, amount: Double) {
        println("Saving expend to DB")
    }
    
    companion object {
      private var instance: ExpendManager? = null
  
      fun getInstance(): ExpendManager {
          if (instance == null) {
              instance = ExpendManager()
          }
          return instance!!
      }
    }
}

fun main() {
    val manager1 = ExpendManager.getInstance()
    val manager2 = ExpendManager.getInstance()
    println(manager1 === manager2)   // Output: true (cùng một instance)
}

Ở đây, constructor của ExpendManager là private, và chúng ta sử dụng getInstance() để lấy Instance duy nhất của lớp.

Bài Học Từ Singleton Pattern

Singleton Pattern giúp chúng ta:

  • Kiểm soát việc tạo ra các đối tượng, đảm bảo chỉ có một Instance.
  • Cung cấp một điểm truy cập toàn cục, dễ dàng.
  • Cần cân nhắc việc lạm dụng Singleton vì nó có thể dẫn đến sự lệ thuộc cao và khó kiểm thử bằng Unit Test. Đồng thời cần quản lý chặt vấn đề Single Instance trong môi trường multi threads

Khi thiết kế, hãy xem xét kỹ lưỡng liệu việc có nhiều Instances của một lớp có gây ra vấn đề hay không. Nếu chỉ cần 1, Singleton Pattern có thể là một giải pháp phù hợp.

Chúc anh em thành công! Nhớ Follow @dantech nha 🙂