+20

Golang Design Patterns - Factory Method. Nơi tạo ra nhiều loại đồ uống từ hạt cafe

I. Factory Method - Creational Pattern

Mẫu thiết kế Factory Method (gọi là Factory cho gọn cũng được) cũng là design pattern rất thường được sử dụng trong các dự án thực tế. Mục đích của Factory đưa người dùng tránh xa việc khởi tạo phức tạp của đối tượng bằng cách abstraction công việc đó, và cung cấp những gì người dùng cần như lấy data từ một nguồn cụ thể, từ web service, database...

Cụ thể hơn, Factory Method tạo ra một higher layer , nơi tập trung cho việc khởi tạo các đối tượng, chúng ta uỷ thác (delegate) việc khởi tạo các loại đối tượng có tính chất tương đồng với nhau cho layer này thay vì phải xử lý ngay tại đối tượng đó. Giống như việc đầu tư tài chính (stock hay cryptocurrency) thông qua bot AI vậy, mình không phải bận tâm việc mua về cổ phiếu của công ty nào hay đồng coin nào cả, cứ để mấy con bot làm hết nhé 😄.

II. Factory Method mang lại cho developers những gì?

Sơ qua một vài điều trên thì mình tóm tắt lại cho các bạn những ý chính sau mà mẫu thiết kế này mang lại:

  • Factory đem tất cả các công việc khởi tạo đối tượng đi một nơi khác (object khác, package khác) mà không để nó nằm ở chính nơi định nghĩa nó.
  • Tất cả đều giao tiếp qua interface mà không phải trực tiếp đi vào công đoạn khởi tạo cụ thể nào cả.
  • Gom nhóm những loại đối tượng tương đồng với nhau về khía cạnh nào đó (cappuccino và latte đều được làm từ hạt cafe).

III. Ví dụ thực tế

Đưa ra một bài toán cụ thể để chúng ta dễ hình dung nhé. Bây giờ chúng ta cần triển khai một máy pha thức uống từ cafe, chiếc máy này có thể làm ra cappuccino và latte tự động cho khách hàng, trên mỗi ly thức uống sẽ có tên của chúng. Mình sẽ đặt tên cho dễ nhớ vậy, gọi máy pha thức uống từ cafe là CoffeeBarFactory, các đối tượng Cappuccino và Latte đều gọi chung là CoffeeDrink - có chung một method là GetName implement từ một interface ICoffeeDrink.

Trước khi đi vào coding thì mình sẽ nói sơ qua một vài điều cần chú ý khi sử dụng Factory Method:

  • Mọi đồ uống (Cappuccino, Latte,...) gọi chung là CoffeeDrink đều cần implement interface ICoffeeDrink, ở đây cụ thể là GetName.
  • Chúng ta uỷ thác mọi việc khởi tạo đối tượng đồ uống cho CoffeeBarFactory.
  • Được phép bổ sung nhiều loại đồ uống khác từ cafe thông qua CoffeeBarFactory.

IV. Implementation

Trước tiên define struct CoffeeDrink và interface ICoffeeDrink. Struct CoffeeDrink sẽ implement method đã được định nghĩa ở ICoffeeDrink.

package factory_method

type CoffeeDrink struct {
	name string
}

func (me *CoffeeDrink) GetName() string {
	return me.name
}
package factory_method

type ICoffeeDrink interface {
	GetName() string
}

Tiếp đến chúng ta sẽ định nghĩa đối tượng Cappuccino và Latte, theo kèm đó là creation function của chúng, lưu ý những function này là private, giấu đi các việc khởi tạo của chúng với bên ngoài.

package factory_method

type Cappuccino struct {
	CoffeeDrink
}

func newCappuccino() *Cappuccino {
	return &Cappuccino{
		CoffeeDrink: CoffeeDrink{
			name: "Cappuccino",
		},
	}
}

package factory_method

type Latte struct {
	CoffeeDrink
}

func newLatte() *Latte {
	return &Latte{
		CoffeeDrink: CoffeeDrink{
			name: "Latte",
		},
	}
}

Cuối cùng là CoffeeBarFactory, ở đây để đơn giản nhất, mình chỉ define một function có tên là GetCoffeeDrink, đương nhiên đây là public accessor function:

package factory_method

import "fmt"

const (
	latteName      = "Latte"
	cappuccinoName = "Cappuccino"
)

func GetCoffeeDrink(name string) (ICoffeeDrink, error) {
	switch name {
	case latteName:
		return newLatte(), nil
	case cappuccinoName:
		return newCappuccino(), nil
	default:
		return nil, fmt.Errorf("unknown coffee drink: %s", name)
	}
}

Vậy là đủ rồi, chúng ta chạy thử chương trình sample nhé:

cappuccino, err := factory_method.GetCoffeeDrink("Cappuccino")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(cappuccino.GetName())
	}

	latte, err := factory_method.GetCoffeeDrink("Latte")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(latte.GetName())
	}

	_, err = factory_method.GetCoffeeDrink("Error")
	if err != nil {
		fmt.Println(err)
	}

Kết quả:

image.png

V. Lời kết

Qua ví dụ ở trên, các bạn đã hình dung được rõ chức năng của design pattern Factory Method rồi, đó là gom nhóm các đối tượng cùng loại và đưa việc khởi tạo (outside of scope). Bên cạnh đó, việc thêm nhiều loại CoffeeDrink sẽ không còn quá phức tạp nữa.

À, ở trên mình ví dụ thôi nhé, không có con bot nào trade lời cho các bạn đâu, như crypto thì chỉ có CZ là con bot AI đó thôi 😄 (just kidding). Cảm ơn các bạn đã xem bài viết .

VI. References

Go Design Patterns (Mario Castro Contreras)


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí