Design Pattern Series - Abstract Factory Pattern (Golang)

Go Design Pattern Series 27 Th05 2025

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: ButtonWindow, cho hai hệ điều hành WindowsmacOS.

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 method Render().
  • Window yêu cầu method Draw().
  • 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:

  • WindowsButtonMacOSButton implement interface Button.
  • WindowsWindowMacOSWindow implement interface Window.
  • 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()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ạo WindowsButtonWindowsWindow.
  • MacOSFactory tạo MacOSButtonMacOSWindow.
  • 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ới UIFactory, không biết đến class cụ thể.
  • main có thể dễ dàng chuyển đổi giữa WindowsFactoryMacOSFactory.

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!

Chuyên mục