Design Pattern Series - Factory Pattern (Golang)
Factory Pattern là gì?
Factory Pattern là một design pattern thuộc nhóm Creational Patterns. Nó cung cấp cách để tạo đối tượng mà không cần chỉ định trực tiếp class cụ thể. Thay vào đó, Factory Pattern sử dụng factory method hoặc factory struct để xác định loại đối tượng cần tạo dựa trên tham số đầu vào hoặc điều kiện.
Trong Go, do không hỗ trợ kế thừa như các ngôn ngữ hướng đối tượng truyền thống, Factory Pattern thường được triển khai bằng functions hoặc structs, kết hợp với interface để đảm bảo tính linh hoạt và trừu tượng.
Mục đích của Factory Pattern
- Trừu tượng hóa việc tạo đối tượng: Giấu đi logic tạo object, giúp code dễ bảo trì và mở rộng.
- Tăng tính linh hoạt: Cho phép thêm object type mới mà không cần sửa code hiện có (Open/Closed Principle).
- Kiểm soát tập trung: Logic tạo object nằm ở một chỗ, dễ quản lý và tối ưu.
- Giảm phụ thuộc: Code phía client chỉ làm việc với interface hoặc abstract type, không cần biết chi tiết implement cụ thể.
Khi nào dùng Factory Pattern?
- Khi cần tạo nhiều object khác nhau có chung một interface.
- Khi logic tạo object phức tạp (ví dụ: khởi tạo với config mặc định hoặc giá trị đặc biệt).
- Khi muốn tách biệt logic tạo object khỏi logic sử dụng object.
Ví dụ thực tế
- Kết nối cơ sở dữ liệu: Tạo connection khác nhau (MySQL, PostgreSQL, SQLite) dựa trên config.
- Xử lý thanh toán: Quản lý nhiều phương thức thanh toán (CreditCard, PayPal) trong hệ thống e-commerce.
Triển khai Factory Pattern trong Go
Giả sử ta có một hệ thống thanh toán hỗ trợ hai loại: CreditCard và PayPal. Mỗi loại đều có method chung là Pay
. Ta sẽ dùng Factory Pattern để tạo object phù hợp dựa trên loại thanh toán được yêu cầu.
Bước 1: Định nghĩa Interface
type PaymentMethod interface {
Pay(amount float64) string
}
Tất cả các phương thức thanh toán phải implement method Pay
.
Bước 2: Tạo các Struct cụ thể
type CreditCardPayment struct{}
func (c *CreditCardPayment) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f using Credit Card", amount)
}
type PayPalPayment struct{}
func (p *PayPalPayment) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f using PayPal", amount)
}
Bước 3: Tạo Factory function
func PaymentMethodFactory(paymentType string) (PaymentMethod, error) {
switch paymentType {
case "creditcard":
return &CreditCardPayment{}, nil
case "paypal":
return &PayPalPayment{}, nil
default:
return nil, fmt.Errorf("unsupported payment method: %s", paymentType)
}
}
Bước 4: Sử dụng Factory trong client code
func main() {
// Credit Card
creditCard, err := PaymentMethodFactory("creditcard")
if err != nil {
log.Fatal(err)
}
fmt.Println(creditCard.Pay(100.50))
// PayPal
payPal, err := PaymentMethodFactory("paypal")
if err != nil {
log.Fatal(err)
}
fmt.Println(payPal.Pay(75.25))
// Phương thức không hỗ trợ
_, err = PaymentMethodFactory("bitcoin")
if err != nil {
fmt.Println(err)
}
}
Giải thích từng bước
- Bước 1: Interface
PaymentMethod
giúp trừu tượng hóa, client chỉ cần làm việc với interface thay vì implementation cụ thể. - Bước 2: Các struct
CreditCardPayment
,PayPalPayment
implement methodPay
. - Bước 3:
PaymentMethodFactory
tập trung toàn bộ logic tạo object. - Bước 4: Client chỉ cần gọi factory, không quan tâm object được tạo thế nào.
Full code
package main
import (
"fmt"
"log"
)
// Định nghĩa interface
type PaymentMethod interface {
Pay(amount float64) string
}
// Implement CreditCardPayment
type CreditCardPayment struct{}
func (c *CreditCardPayment) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f using Credit Card", amount)
}
// Implement PayPalPayment
type PayPalPayment struct{}
func (p *PayPalPayment) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f using PayPal", amount)
}
// Factory function
func PaymentMethodFactory(paymentType string) (PaymentMethod, error) {
switch paymentType {
case "creditcard":
return &CreditCardPayment{}, nil
case "paypal":
return &PayPalPayment{}, nil
default:
return nil, fmt.Errorf("unsupported payment method: %s", paymentType)
}
}
func main() {
creditCard, err := PaymentMethodFactory("creditcard")
if err != nil {
log.Fatal(err)
}
fmt.Println(creditCard.Pay(100.50))
payPal, err := PaymentMethodFactory("paypal")
if err != nil {
log.Fatal(err)
}
fmt.Println(payPal.Pay(75.25))
_, err = PaymentMethodFactory("bitcoin")
if err != nil {
fmt.Println(err)
}
}
Output
Paid 100.50 using Credit Card
Paid 75.25 using PayPal
unsupported payment method: bitcoin
Kết luận
Factory Pattern là công cụ mạnh mẽ để quản lý việc tạo object trong Go, đặc biệt khi cần hỗ trợ nhiều loại object có chung một interface. Nó giúp code dễ mở rộng, dễ bảo trì, và tuân theo nguyên tắc thiết kế hướng đối tượng.
Có thể mở rộng ví dụ này bằng cách:
- Thêm phương thức thanh toán mới (BankTransfer, Crypto).
- Thêm logic khởi tạo phức tạp hơn trong factory.
- Sử dụng config hoặc tham số để tùy chỉnh object được tạo.
Bài tiếp theo trong series Abstract Factory Pattern