Kotlin Design Pattern - P1 - Creational

I. Dẫn nhập

1. Design pattern là gì?

  • Design pattern là tập hợp các mẫu thiết kế lập trình dùng để giải quyết các vấn đề thường gặp trong lập trình để nhằm tăng năng suất, giúp code chất lượn hơn, giảm tiêu tốn tài nguyên trong quá trình maintain nói chung là có nhiều cái lợi. Cần phân biệt khái niệm này với Template là triển khai cụ thể tương ứng với từng lại ngôn ngữ hoặc từng loại trường hợp trong khi Design pattern là thiết kế mang tính tổng quát.

2. Danh mục các design partern

  • Design pattern chia ra làm 3 loại chính: Creational patterns, Structural patterns, Behavioral patterns
    • Creational Design Patterns bao gồm nhóm các patterns dùng cho các cơ chế tạo ra các đối tượng hiệu quả, làm tăng sự linh hoạt và tái sử dụng mã hiện có. Trong bài này mình sẽ giới thiệu các bạn 3 parterns thuộc nhóm Creational: Singleton, Builder, Prototype
    • Structural patterns bao gồm nhóm các patterns dùng để xây dựng hệ thống phân cấp class đơn giản và hiệu quả và mối quan hệ giữa các class khác nhau.
    • Behavioral patterns bao gồm nhóm các patterns dùng để phân bố các hành vi có hiệu quả và sẽ tăng tính linh hoạt trong việc thực hiện giao tiếp này.

II. Creational Design Pattern P1

1. SingleTon

a. Định nghĩa

  • Singleton là một mẫu thiết kế creational cho phép bạn đảm bảo rằng một lớp chỉ có một object thể hiện và cung cấp truy cập object này với phạm vi toàn ứng dụng.

b. Khi nào cần áp dụng

  • Hầu hết các đối tượng trong một ứng dụng đều chịu trách nhiệm cho công việc của chúng và truy xuất dữ liệu tự lưu trữ (self-contained data) và các tham chiếu trong phạm vi được đưa ra của chúng. Tuy nhiên, có nhiều đối tượng có thêm những nhiệm vụ và có ảnh hưởng rộng hơn, chẳng hạn như quản lý các nguồn tài nguyên bị giới hạn hoặc theo dõi toàn bộ trạng thái của hệ thống. Những nhiệm vụ của các đối tượng này thường yêu cầu chỉ có một đối tượng của một lớp. Các ví dụ bao gồm đối tượng request call API, đối tượng play âm thanh trong ứng dụng game,... Những phần khác trong ứng dụng phụ thuộc vào những đối tượng đặc biệt này và cần có cách để tìm ra chúng. Đây là nơi mà mẫu thiết kế Singleton được sử dụng.
  • Bạn gặp một sự cố về hiệu năng hệ thống. Cùng một thời điểm, các bạn đang sử dụng một lúc nhiều đối tượng và chúng làm tiêu tốn quá nhiều tài nguyên của hệ thống. Đây là vấn đề mà bạn cần phải khắc phục, và Singleton pattern có thể giúp bạn thực hiện được điều đó.

c. Implement bằng kotlin

class SimpleSingleton.kt:

object SimpleSingleton {

    private var instance: SimpleSingleton? = null

    @Synchronized fun getInstance(): SimpleSingleton? {
        if (instance == null) {
            instance = SimpleSingleton
        }
        return instance
    }
}

class thực thi Singleton.kt:

    @JvmStatic fun main(args: Array<String>) {
        val simpleSingleton = SimpleSingleton.getInstance()
    }

2. Builder

a. Định nghĩa

  • Builder là một mẫu thiết kế creational cho phép bạn tạo ra các loại và biểu diễn khác nhau của một đối tượng sử dụng cùng một quá trình xây dựng. Builder cho phép xây dựng các đối tượng phức tạp từng bước.

b. Khi nào cần áp dụng ?

  • Trong những phần mềm lớn, với những chức năng phức tạp và nhiều đối tượng, việc khởi tạo ứng dụng gặp nhiều khó khăn. Chúng ta không thể dồn tất cả công việc khởi tạo này cho một hàm khởi tạo. Vì như thế sẽ rất khó kiểm soát hết, và hơn nữa không phải lúc nào các thành phần của ứng dụng cũng được khởi tạo một cách đồng bộ. Có thành phần được tạo ra vào lúc dịch chương trình nhưng cũng có thành phần tuỳ theo từng yêu cầu của người dùng, tuỳ vào hoàn cảnh của ứng dụng, mà nó sẽ được tạo ra. Do vậy người ta giao công việc này cho một đối tượng chịu trách nhiêm khởi tạo, và chia việc khởi tạo ứng dụng riêng rẽ, để có thể tiến hành khởi tạo riêng biệt ở các hoàn cảnh khác nhau. Giải pháp đưa ra là sử dụng Builder Pattern như một người xây dựng.

c. Implement bằng kotlin

  • Bài toán ở đây mình áp dụng Builder partern vào việc khởi tạo object Person có các thuộc tính: tên, tuổi, địa chỉ

class Person.kt

class Person(builder: Person.Builder) {

    private val mName: String?
    private val mAge: Int?
    private val mAddress: String?

    init {
        mName = builder.mName
        mAge = builder.mAge
        mAddress = builder.mAddress
    }

    override fun toString(): String {
        return "mName: $mName,mAge: $mAge,mAddress $mAddress"
    }

    /**
     * Builder class
     */
    class Builder {
        var mName: String? = null
        var mAge: Int = 0
        var mAddress: String? = null

        fun widthName(name: String): Builder {
            this.mName = name
            return this
        }

        fun widthAge(age: Int): Builder {
            this.mAge = age
            return this
        }

        fun widthAddress(address: String): Builder {
            this.mAddress = address
            return this
        }

        fun build(): Person {
            return Person(this)
        }
    }
}

class thực thi Builder.kt:

object Builder {

  @JvmStatic fun main(args: Array<String>) {
      // Create object person with name
    val person: Person = Person.Builder().widthName("DaoLQ").build()
    print(person.toString() + "\n\n")
      // Create object person with full info
    val person: Person = Person.Builder().widthName("DaoLQ").widthAddress("Phu Loc 9").widthAge(
        27).build()
    print(person.toString() + "\n\n")
  }
}

3. Prototype

a. Định nghĩa

  • Prototype Pattern là một trong những pattern phổ biến trong lập trình hướng đối tượng, là một pattern thuộc nhóm Creational Patterns. Ý tưởng này là một design pattern đặc biệt có liên quan đến việc khởi tạo đối tượng (Object), thay vì tạo ra Object, Prototype pattern sử dụng việc cloning (copy nguyên mẫu của Object)

b. Khi nào cần áp dụng ?

  • Thay cho việc tạo ra một đối tượng không được thiết lập, nó trả về một đối tượng mới đã được thiết lập với các giá trị mà nó đã sao chép từ một đối tượng kiểu mẫu, cụ thể như bạn muốn tạo object sinh viên mới tất cả thông tin giống vs 1 object có sẵn chỉ khác mỗi tên, thì áp dụng mẫu này và set lại tên cho object clone là xong
  • Khi truyền object vào method, trong method này có cập nhật lại dữ liệu cho object để thực thi nếu truyền trực tiếp object vào thì dữ liệu của object truyền vào sẽ thay đổi do cùng tham chiếu vs object dùng trong method, lúc này ta nên clone object truyền vào (áp dụng prototype pattern) và sử dụng để tránh side effect.

c. Implement bằng kotlin

  • Trong kolin hay java đều có sẵn interface Cloneable dùng cho việc áp dụng partern này, Example dưới đây mình show các bạn thấy việc áp dụng prototype partern sẽ tạo ra 1 đối tượng mới

class PersonClone.kt

class PersonClone(val name: String) : Cloneable {

    @Throws(CloneNotSupportedException::class)
    public override fun clone(): PersonClone {
        return super.clone() as PersonClone
    }
}

class thực thi Prototype.kt:

object Prototype {

    @JvmStatic fun main(args: Array<String>) {
        var person = PersonClone("DaoLQ")
        print(person.name + " " + person.hashCode())
        person = person.clone()
        print("\n" + person.name + " " + person.hashCode() + "\n\n")
    }
}

4. Source Code:

Github - KotlinDesignPattern

III. Kết:

  • Mình tạm dừng ở đây ở bài sau mình sẽ giới thiệu tiếp với các bạn về các partern khởi tạo thường dùng còn lại: Factory Method, Abstract Factory và Object Pool