Design Pattern Series - Abstract Factory Pattern (Golang)
Bài viết trước trong series: Factory Pattern
Abstract Factory Pattern là gì?
Abstract Factory Pattern cung cấp một interface để tạo ra các nhóm đối tượng liên quan hoặc phụ thuộc mà không cần chỉ định trực tiếp class cụ thể.
Thay vì tạo object trực tiếp, pattern này sử dụng một factory để sinh ra các object thuộc cùng một “gia đình” (family).
Ví dụ: nếu bạn xây dựng một ứng dụng UI cho nhiều hệ điều hành (Windows, macOS), Abstract Factory Pattern cho phép bạn tạo các UI components như Button hoặc Window phù hợp với từng hệ điều hành, mà không cần thay đổi code logic chính.
Đặc điểm chính
- Trừu tượng hóa việc tạo object: Client chỉ làm việc với interface, giảm phụ thuộc vào concrete class.
- Tạo nhóm object liên quan: Mỗi factory sinh ra một tập object thuộc cùng một family, đảm bảo tính nhất quán.
- Linh hoạt: Dễ dàng chuyển đổi hoặc mở rộng các nhóm object ở runtime mà không cần sửa client code.
Lợi ích và trường hợp sử dụng
Lợi ích
- Đảm bảo tính nhất quán: Các object được tạo luôn thuộc cùng một family, tránh tình trạng “lẫn lộn” (ví dụ: dùng Windows Button với macOS Window).
- Tăng tính trừu tượng: Client chỉ tương tác qua interface → dễ bảo trì, mở rộng, giảm phụ thuộc.
- Giảm độ phức tạp: Client không cần biết chi tiết cách tạo object, chỉ cần chọn đúng factory.
Trường hợp sử dụng
- Xây dựng cross-platform UI: Tạo các UI components (Button, Window, Scrollbar) cho Windows, macOS, Linux.
- Hệ thống có nhiều biến thể: Ví dụ: tạo nhóm sản phẩm như động cơ (Engine), bánh xe (Wheel) cho sedan hoặc SUV.
- Thay đổi cấu hình động: Khi cần chuyển đổi cả nhóm object ở runtime mà không cần chỉnh sửa code.
Triển khai Abstract Factory Pattern trong Go
Ví dụ: ta sẽ xây dựng một hệ thống UI với hai loại sản phẩm: Button và Window, cho hai hệ điều hành Windows và macOS.
Bước 1: Định nghĩa các Interface cho sản phẩm
// products.go
package main
type Button interface {
Render() string
}
type Window interface {
Draw() string
}
Giải thích:
Button
yêu cầu methodRender()
.Window
yêu cầu methodDraw()
.- Các concrete product (WindowsButton, MacOSWindow) sẽ implement các interface này.
Bước 2: Tạo các Concrete Products
// windows_button.go
package main
type WindowsButton struct{}
func (b WindowsButton) Render() string {
return "Rendering a Windows button"
}
// macos_button.go
package main
type MacOSButton struct{}
func (b MacOSButton) Render() string {
return "Rendering a macOS button"
}
// windows_window.go
package main
type WindowsWindow struct{}
func (w WindowsWindow) Draw() string {
return "Drawing a Windows window"
}
// macos_window.go
package main
type MacOSWindow struct{}
func (w MacOSWindow) Draw() string {
return "Drawing a macOS window"
}
Giải thích:
WindowsButton
vàMacOSButton
implement interfaceButton
.WindowsWindow
vàMacOSWindow
implement interfaceWindow
.- Mỗi family (Windows, macOS) đều có sản phẩm riêng, đảm bảo tính nhất quán.
Bước 3: Định nghĩa Abstract Factory Interface
// ui_factory.go
package main
type UIFactory interface {
CreateButton() Button
CreateWindow() Window
}
Giải thích:
UIFactory
định nghĩa 2 method:CreateButton()
vàCreateWindow()
.- Concrete Factory sẽ implement interface này để tạo sản phẩm cho từng family.
Bước 4: Tạo các Concrete Factories
// windows_factory.go
package main
type WindowsFactory struct{}
func (f WindowsFactory) CreateButton() Button {
return WindowsButton{}
}
func (f WindowsFactory) CreateWindow() Window {
return WindowsWindow{}
}
// macos_factory.go
package main
type MacOSFactory struct{}
func (f MacOSFactory) CreateButton() Button {
return MacOSButton{}
}
func (f MacOSFactory) CreateWindow() Window {
return MacOSWindow{}
}
Giải thích:
WindowsFactory
tạoWindowsButton
vàWindowsWindow
.MacOSFactory
tạoMacOSButton
vàMacOSWindow
.- Mỗi factory sinh ra toàn bộ sản phẩm trong family của nó.
Bước 5: Sử dụng Abstract Factory trong client code
// main.go
package main
import "fmt"
func createUI(factory UIFactory) {
button := factory.CreateButton()
window := factory.CreateWindow()
fmt.Println(button.Render())
fmt.Println(window.Draw())
}
func main() {
// UI cho Windows
windowsFactory := WindowsFactory{}
createUI(windowsFactory)
// UI cho macOS
macOSFactory := MacOSFactory{}
createUI(macOSFactory)
}
Giải thích:
- Client code (
createUI
) chỉ làm việc vớiUIFactory
, không biết đến class cụ thể. main
có thể dễ dàng chuyển đổi giữaWindowsFactory
vàMacOSFactory
.
Kết quả chạy chương trình
Rendering a Windows button
Drawing a Windows window
Rendering a macOS button
Drawing a macOS window
Giải thích:
- Khi dùng
WindowsFactory
→ sinh ra toàn bộ UI Windows. - Khi dùng
MacOSFactory
→ sinh ra toàn bộ UI macOS. - Client code không thay đổi, nhưng family của sản phẩm có thể thay thế dễ dàng.
Kết luận
Abstract Factory Pattern cho phép tạo các nhóm object liên quan một cách linh hoạt và nhất quán.
Trong Go, pattern này đặc biệt hữu ích khi cần xây dựng hệ thống có nhiều biến thể (multi-variant) hoặc cross-platform.
Bằng việc dùng Abstract Factory, code trở nên:
- Dễ mở rộng (thêm family mới chỉ cần thêm factory).
- Dễ bảo trì (client không phụ thuộc vào implementation cụ thể).
- Đảm bảo tính thống nhất (toàn bộ object đến từ cùng một family).
Bài viết sau trong series: Singleton Pattern:
Thanks for reading!