Prototype Pattern là gì?

Wikipedia định nghĩa prototype Pattern như sau:

The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects

Chúng ta có thể hiểu nôm na là Prototype pattern sẽ tạo ra một object mới bằng cách copy, object mới sẽ mang đầy đủ đặc tính của object ban đầu, và chúng ta có thể thay đổi object mới mà không ảnh hưởng gì đến object ban đầu

Prototype pattern Image Demo

Prototype Pattern được sử dụng khi nào?

  • Được dùng khi việc tạo một object tốn nhiều chi phí và thời gian trong khi bạn đã có một object tương tự tồn tại.
  • Muốn truyền đối tượng vào một hàm nào đó để xử lý, thay vì truyền đối tượng gốc có thể ảnh hưởng dữ liệu thì ta có thể truyền đối tượng sao chép.
  • Ẩn độ phức tạp của việc khởi tạo đối tượng từ phía Client.
  • ...

Cài đặt Prototype Pattern

Hình ảnh minh hoa các thành phần của Prototype Pattern
Hình ảnh minh hoa các thành phần của Prototype Pattern

Một Prototype Pattern gồm các thành phần cơ bản sau:

  • Prototype : khai báo một class, interface hoặc abtract class cho việc clone chính nó.
  • ConcretePrototype : phần này sẽ implement cho method clone được khai báo ở interface
  • Client : có thể copy bất kì object nào thông qua phương thức clone

Ví dụ demo

Một công ty bất động sản muốn xây dựng các ngôi nhà liền kề giống nhau, mỗi ngôi nhà bao gồm các thông tin: số lượng phòng ngủ (bedroom), phòng khách (living room). Tùy vào nhu cầu của khách mua nhà mà số lượng phòng ngủ hay phòng khách có thể thay đổi được

Cách làm này được minh họa như sau:

  • Đầu tiên chúng ta định nghĩa interface iHouse
  • Tiếp theo, chúng ta sẽ tạo một đối tượng normalHouse implement interface iHouse
  • Sau đó, mỗi khi muốn xây ngôi nhà mới, chỉ việc clone() từ ngôi nhà chuẩn đã tạo ban đầu. Tùy theo, nhu cầu của mỗi khách hàng có thể thay đổi số lượng phòng lại cho phù hợp (SetLivingRoom(), SetBedRoom())

Triển khai

iHouse.go : Định nghĩa iHouse interface với danh sách method

package house

type iHouse interface {
    Print()
    SetLivingRoom(int)
    SetBedRoom(int)
    Clone() iHouse
}

normal_house.go

package house

import "fmt"

type NormalHouse struct {
    LivingRoom int
    BedRoom int
}

func (h *NormalHouse) Print() {
    fmt.Printf("%d living room - %d bed room\n", h.LivingRoom, h.BedRoom)
}

func (h *NormalHouse) SetLivingRoom(room int) {
    h.LivingRoom = room
}

func (h *NormalHouse) SetBedRoom(room int) {
    h.BedRoom = room
}

func (h *NormalHouse) Clone() iHouse {
    return &NormalHouse{
        LivingRoom: h.LivingRoom,
        BedRoom: h.BedRoom,
    }
}

Ở đây chúng ta khai báo đối tượng NormalHouse với 2 thuộc tính : LivingRoom, BedRoom

Đồng thời khai báo tất cả các phương thức để inplement interface iHouse

main.go

package main

import (
    "prototype-pattern/house"
)

func main() {
    // Tạo 1 ngôi nhà với 1 phòng khách và 1 phòng ngủ
    normalHouse := house.NormalHouse {
        LivingRoom : 1,
        BedRoom : 1,
    }
    normalHouse.Print() // 1 living room - 1 bed room

    // Clone 1 ngôi nhà từ ngôi nhà đã tạo từ trước
    normalHouseClone := normalHouse.Clone()
    normalHouseClone.Print() // 1 living room - 1 bed room

    // Thay đôi số lượng phòng khách và phòng ngủ của ngôi nhà vừa clone
    normalHouseClone.SetBedRoom(2)
    normalHouseClone.SetLivingRoom(3)
    normalHouseClone.Print() // 3 living room - 2 bed room

    // Kiểm tra lại ngôi nhà ban đầu
    normalHouse.Print() // 1 living room - 1 bed room
}

File main.go đóng vai trò như là một client, ở đây chúng ta sẽ tiến hành tạo ra một ngôi nhà chuẩn ban đầu normalHouse với (1 living room, 1 bed room)

Tiếp theo, nếu chúng ta muốn tạo ra nhưng ngôi nhà khác (normalHouseClone) từ ngôi nhà ban đầu normalHouse, thì chỉ việc gọi phương thức Clone()

Trường hợp muốn thay đổi số lượng living room, bed room, chúng ta chỉ cần truyền số lượng mong muốn vào phương thức SetBedRoom(), SetLivingRoom()

Kết quả đạt được

1 living room - 1 bed room
1 living room - 1 bed room
3 living room - 2 bed room
1 living room - 1 bed room

Ưu và nhược điểm của pattern này

Ưu điểm

  • Có thể dễ dàng sao chép 1 đối tượng mà không cần quan tâm đến class cụ thể
  • Có thể loại bỏ các đoạn code lặp đi lặp lại khi khởi tạo đối tượng
  • Có thể tạo ra các đối tượng phức tạp một cách thuận tiện và nhanh chóng
  • Đây có thể coi là một giải pháp cho kế thừa

Nhược điểm

  • Nếu object có tham chiếu hoặc kế thừa từ các object khác. Clone thì dễ, nhưng không biết mức độ ảnh hưởng tới đâu. Nếu object clone có dây mơ rễ má nhiều chỗ -> rất dễ trở thành một đám tơ vò.