Design Pattern Series - Abstract Factory Pattern (Golang)

Go Design Pattern Series May 27, 2025

Previous post in series: Factory Pattern

Abstract Factory Pattern cung cấp một interface để tạo ra các nhóm đối tượng có liên quan hoặc phụ thuộc lẫn nhau mà không cần chỉ định trực tiếp các lớp cụ thể. Thay vì tạo từng đối tượng riêng lẻ, mẫu thiết kế này sử dụng một "nhà máy" để tạo ra cả một "gia đình" đối tượng.

Ví dụ: Nếu bạn xây dựng một ứng dụng giao diện (UI) cho nhiều hệ điều hành (Windows, macOS), Abstract Factory cho phép bạn tạo ra các thành phần như Button hoặc Window phù hợp với từng hệ điều hành đó mà không làm thay đổi mã nguồn logic chính.

Các đặc điểm chính

  • Trừu tượng hóa việc khởi tạo: Client chỉ làm việc với các interface, giảm sự phụ thuộc vào các lớp triển khai cụ thể (concrete classes).
  • Tạo nhóm đối tượng liên quan: Mỗi factory tạo ra một bộ đối tượng thuộc cùng một gia đình, đảm bảo tính đồng nhất.
  • Linh hoạt: Dễ dàng chuyển đổi hoặc mở rộng các nhóm đối tượng tại thời điểm chạy (runtime) mà không cần sửa đổi mã nguồn của client.

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 đối tượng được tạo ra luôn thuộc cùng một gia đình, tránh việc "trộn lẫn" (ví dụ: dùng Button của Windows với Window của macOS).
  • Tăng tính trừu tượng: Client chỉ tương tác qua interface → dễ bảo trì, mở rộng và 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 ra các đối tượng, chỉ cần chọn đúng factory phù hợp.

Trường hợp sử dụng

  • Xây dựng UI đa nền tảng: Tạo các thành phần UI (Button, Window, Scrollbar) cho Windows, macOS, Linux.
  • Hệ thống có nhiều biến thể: Ví dụ, tạo các nhóm phụ tùng như Engine, Wheel cho dòng xe Sedan hoặc SUV.
  • Thay đổi cấu hình động: Khi bạn cần thay đổi toàn bộ nhóm đối tượng tại thời điểm chạy mà không muốn sửa code.

Triển khai Abstract Factory Pattern trong Go

Chúng ta sẽ xây dựng một hệ thống UI với hai loại sản phẩm: ButtonWindow, dành cho hai hệ điều hành: WindowsmacOS.

Bước 1: Khai báo Interface cho Button và Window

// products.go
package main

type Button interface {
    Render() string
}

type Window interface {
    Draw() string
}
  • Button yêu cầu phương thức Render().
  • Window yêu cầu phương thức Draw().

Bước 2: Tạo các sản phẩm cụ thể (Concrete Products)

// windows_products.go
type WindowsButton struct{}
func (b WindowsButton) Render() string { return "Đang hiển thị Button kiểu Windows" }

type WindowsWindow struct{}
func (w WindowsWindow) Draw() string { return "Đang vẽ Window kiểu Windows" }

// macos_products.go
type MacOSButton struct{}
func (b MacOSButton) Render() string { return "Đang hiển thị Button kiểu macOS" }

type MacOSWindow struct{}
func (w MacOSWindow) Draw() string { return "Đang vẽ Window kiểu macOS" }

Bước 3: Định nghĩa Interface cho Abstract Factory

// ui_factory.go
type UIFactory interface {
    CreateButton() Button
    CreateWindow() Window
}

UIFactory định nghĩa hai phương thức: CreateButton()CreateWindow(). Các Factory cụ thể sẽ triển khai interface này.

Bước 4: Tạo các Factory cụ thể (Concrete Factories)

// windows_factory.go
type WindowsFactory struct{}
func (f WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (f WindowsFactory) CreateWindow() Window { return WindowsWindow{} }

// macos_factory.go
type MacOSFactory struct{}
func (f MacOSFactory) CreateButton() Button { return MacOSButton{} }
func (f MacOSFactory) CreateWindow() Window { return MacOSWindow{} }

Bước 5: Sử dụng Abstract Factory

// 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() {
    // Chạy UI cho Windows
    fmt.Println("--- Windows UI ---")
    windowsFactory := WindowsFactory{}
    createUI(windowsFactory)

    // Chạy UI cho macOS
    fmt.Println("\n--- macOS UI ---")
    macOSFactory := MacOSFactory{}
    createUI(macOSFactory)
}

Giải thích kết quả

Khi chạy chương trình, output sẽ là:

Plaintext

--- Windows UI ---
Đang hiển thị Button kiểu Windows
Đang vẽ Window kiểu Windows

--- macOS UI ---
Đang hiển thị Button kiểu macOS
Đang vẽ Window kiểu macOS
  • Khi sử dụng WindowsFactory, toàn bộ hệ thống UI của Windows được khởi tạo.
  • Khi chuyển sang MacOSFactory, toàn bộ hệ thống UI của macOS được tạo ra.
  • Mã nguồn phía Client (createUI) hoàn toàn không đổi, giúp việc thay đổi "gia đình" sản phẩm trở nên cực kỳ linh hoạt.

Kết luận

Abstract Factory Pattern cho phép tạo ra các nhóm đối tượng liên quan một cách nhất quán và linh hoạt. Trong Go, mẫu thiết kế này đặc biệt hữu ích khi xây dựng các hệ thống đa biến thể hoặc đa nền tảng.

Bằng cách sử dụng Abstract Factory, mã nguồn của bạn sẽ:

  1. Dễ dàng mở rộng: Muốn thêm một hệ điều hành mới (như Linux), bạn chỉ cần tạo thêm Factory mới.
  2. Dễ bảo trì: Client không phụ thuộc vào các triển khai cụ thể.
  3. Đảm bảo tính đồng bộ: Tất cả các đối tượng luôn được khởi tạo từ cùng một gia đình.

Hy vọng bài viết này giúp bạn hiểu rõ hơn về cách áp dụng Abstract Factory trong dự án Go của mình. Hẹn gặp lại trong các bài viết tiếp theo!

Next post in series: Singleton Pattern:

Tags