Factory patterns trong golang

February 22, 2018
design pattern Golang

Factory patterns là một design pattern mà hầu như tất cả các dự án đều sử dụng. Nếu bạn đã từng biết đến các khái niệm như interface hay implements thì chắc hẳn design pattern này đã ko còn gì xa lạ. Để tạo ra những project code cleaner thì design patterns rõ ràng không thể thiếu trong mọi tư duy của developer. Hôm nay mình sẽ giới thiệu và đi xâu vào chi tiết về factory patterns trong golang.

Mô tả về factory patterns

Không giống các ngôn ngữ khác, Go ko sử dụng class mà được tạo dựng các object thông qua struct và interface. các lớp interface thường là định hướng trước đối tượng và sẽ có nhiều cách thức để implements 1 interface.

Một ví dụ đơn giản, bạn có 1 đối tượng động vật tương tự như là 1 interface. từ lớp động vật đó bạn có thể tạo ra 1 động vật cụ thể như con chó, con mèo, con gà, … Các implements này nó nhiệm vụ thực thi và mô tả chi tiết 1 lớp interface đã được định nghĩa. Trong ứng dụng của bạn nếu nó phát triển mở rộng buộc lòng bạn phải làm quen với pattern này. Hãy tưởng tượng có một interface động vật. Bạn cũng chưa rõ ràng được sử dụng động vật gì, tuy nhiên động vật nào cũng có chân, tay, mắt, mũi, … Sau khi implements con chó thay cho interface động vật thì các phương thức chân, tay, mắt, mũi sẽ được định hình rõ hơn và cụ thể hơn.

Ví dụ đơn giản

Để mô tả chi tiết hơn mình sẽ khởi tạo 1 factory pattern. Giả xử mình có interface animal

type Animal interface {
    run()
}

Sau khi có interface mình cần tạo thêm 1 implements cho interface trên

type dog struct {
    name: string
    age: int
}

func (d *dog) run() {
    // todo something
}

và bây giờ việc cần làm là làm thế nào để kết hợp interface và struct

func NewDog() Animal {
    return dog {
        name: husky,
        age: 2
    }
}

Method khởi tạo trên có ý nghĩa khởi tạo 1 động vật các bạn có thể thấy nó return 1 interface và kết quả return chính là struct của interface đó.

Sử dụng trong các methods các bạn có thể gộp lại và làm như sau

type Animal interface {
    run()
}

type dog struct {
    name: string
    age: int
}

func (d *dog) run() {
    // todo something
    fmt.Printf("Hi, Hello my name %s", d.name)
}

// Factory function
func NewDog() Animal {
    return dog {
        name: husky,
        age: 2
    }
}

func ActionAnimal(a Animal) {
    a.run()
}

func main () {
    dog := NewDog()
    ActionAnimal(dog)
}

// Out put

Hi, Hello husky

Multiple implementations

Khi return về 1 interface, điều đó có nghĩa là chúng ta có thể tạo nhiều những factory function với nhiều implementations khác nhau.

vẫn với các ví dụ ở trên tuy nhiên bây giờ mình sẽ tạo ra 1 implementation là cat


type cat struct {
    name: string
    age: int
}

func (c *cat) run() {
    // todo something
    fmt.Printf("Hi, %s is  %s year old", c.name, c.age)
}

// Factory function
func NewCat() Animal {
    return cat {
        name: tom,
        age: 2
    }
}

func main () {
    dog := NewDog()
    cat := NewCat()
    ActionAnimal(dog)
    ActionAnimal(cat)
}

Kết luận

Trong các project factory patterns là rất cần thiết, thay vì xử dụng trực tiếp các struct. Các bạn hoàn toàn có thể tạo ra các interface. Điều đó có nghĩa tất cả các cấu trúc trong project của các bạn được giao tiếp với nhau thông qua bởi các interface. Điều này giúp project của các bạn sạch sẽ và tối ưu hơn. Đôi khi bạn cần phải mở rộng ứng dụng của mình lên factory patterns sẽ đảm bảo cho code logic của bạn ko hề bị thay đổi. Việc cần làm là hãy viết các implementations khác để tối ưu hơn cho project của mình.

Hi vọng bài viết phần nào mang lại kiến thức bổ ích cho các bạn.