Chuyển tới nội dung chính

Design Patterns trong Go - Giải Pháp Cho Các Vấn Đề Phổ Biến! 🎯

Chào mừng bạn đến với bài học về Design Patterns trong Go! Trong bài học này, chúng ta sẽ tìm hiểu về các mẫu thiết kế phổ biến và cách áp dụng chúng trong Go.

Creational Patterns (Mẫu Tạo Lập) 🏗️

1. Singleton - Đảm Bảo Chỉ Có Một Instance

type Singleton struct {
data string
}

var instance *Singleton
var once sync.Once

func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{
data: "đã khởi tạo",
}
})
return instance
}

💡 Giải thích:

  • Đảm bảo chỉ có một instance
  • Thread-safe
  • Lazy initialization

2. Factory - Tạo Đối Tượng Linh Hoạt

type Product interface {
Name() string
}

type ConcreteProductA struct{}
func (p *ConcreteProductA) Name() string {
return "Sản phẩm A"
}

type ConcreteProductB struct{}
func (p *ConcreteProductB) Name() string {
return "Sản phẩm B"
}

func CreateProduct(productType string) Product {
switch productType {
case "A":
return &ConcreteProductA{}
case "B":
return &ConcreteProductB{}
default:
return nil
}
}

💡 Giải thích:

  • Tạo đối tượng linh hoạt
  • Ẩn chi tiết tạo lập
  • Dễ dàng mở rộng

Structural Patterns (Mẫu Cấu Trúc) 🏗️

1. Adapter - Kết Nối Các Interface

type OldInterface interface {
SpecificRequest() string
}

type NewInterface interface {
Request() string
}

type Adapter struct {
oldInterface OldInterface
}

func (a *Adapter) Request() string {
return a.oldInterface.SpecificRequest()
}

💡 Giải thích:

  • Kết nối interface cũ và mới
  • Không thay đổi code cũ
  • Dễ dàng tích hợp

2. Bridge - Tách Biệt Implementation

type Implementor interface {
OperationImpl() string
}

type Abstraction struct {
impl Implementor
}

func (a *Abstraction) Operation() string {
return a.impl.OperationImpl()
}

💡 Giải thích:

  • Tách biệt abstraction và implementation
  • Dễ dàng thay đổi implementation
  • Giảm phụ thuộc

Behavioral Patterns (Mẫu Hành Vi) 🎭

1. Observer - Thông Báo Thay Đổi

type Observer interface {
Update(data interface{})
}

type Subject struct {
observers []Observer
}

func (s *Subject) Attach(o Observer) {
s.observers = append(s.observers, o)
}

func (s *Subject) Notify(data interface{}) {
for _, o := range s.observers {
o.Update(data)
}
}

💡 Giải thích:

  • Thông báo thay đổi
  • Loose coupling
  • Dễ dàng mở rộng

2. Strategy - Thay Đổi Thuật Toán

type Strategy interface {
Execute(data interface{}) interface{}
}

type Context struct {
strategy Strategy
}

func (c *Context) SetStrategy(s Strategy) {
c.strategy = s
}

func (c *Context) ExecuteStrategy(data interface{}) interface{} {
return c.strategy.Execute(data)
}

💡 Giải thích:

  • Thay đổi thuật toán
  • Runtime switching
  • Dễ dàng thêm mới

Concurrency Patterns (Mẫu Đồng Thời) ⚡

1. Worker Pool - Quản Lý Goroutines

type WorkerPool struct {
jobs chan func()
results chan interface{}
wg sync.WaitGroup
}

func NewWorkerPool(numWorkers int) *WorkerPool {
pool := &WorkerPool{
jobs: make(chan func(), 100),
results: make(chan interface{}, 100),
}

pool.wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go func() {
defer pool.wg.Done()
for job := range pool.jobs {
result := job()
pool.results <- result
}
}()
}

return pool
}

💡 Giải thích:

  • Quản lý goroutines
  • Tái sử dụng workers
  • Kiểm soát tài nguyên

2. Pipeline - Xử Lý Dữ Liệu Tuần Tự

func pipeline(input <-chan int) <-chan int {
output := make(chan int)
go func() {
defer close(output)
for v := range input {
output <- process(v)
}
}()
return output
}

💡 Giải thích:

  • Xử lý dữ liệu tuần tự
  • Tối ưu bộ nhớ
  • Dễ dàng mở rộng

Error Handling Patterns (Mẫu Xử Lý Lỗi) ⚠️

1. Error Wrapper - Bọc Lỗi

type AppError struct {
Code int
Message string
Err error
}

func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}

💡 Giải thích:

  • Thêm context cho lỗi
  • Phân loại lỗi
  • Dễ dàng xử lý

2. Error Chain - Chuỗi Lỗi

func wrapError(err error, msg string) error {
if err == nil {
return nil
}
return fmt.Errorf("%s: %w", msg, err)
}

💡 Giải thích:

  • Tạo chuỗi lỗi
  • Giữ nguyên context
  • Dễ dàng debug

Resource Management Patterns (Mẫu Quản Lý Tài Nguyên) 💾

1. Resource Pool - Quản Lý Tài Nguyên

type ResourcePool struct {
resources chan interface{}
factory func() interface{}
}

func NewResourcePool(factory func() interface{}, size int) *ResourcePool {
pool := &ResourcePool{
resources: make(chan interface{}, size),
factory: factory,
}

for i := 0; i < size; i++ {
pool.resources <- factory()
}

return pool
}

💡 Giải thích:

  • Tái sử dụng tài nguyên
  • Giảm overhead
  • Kiểm soát số lượng

2. Resource Manager - Quản Lý Tài Nguyên Chung

type ResourceManager struct {
resources map[string]interface{}
mu sync.RWMutex
}

func (rm *ResourceManager) Get(key string) interface{} {
rm.mu.RLock()
defer rm.mu.RUnlock()
return rm.resources[key]
}

func (rm *ResourceManager) Set(key string, value interface{}) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.resources[key] = value
}

💡 Giải thích:

  • Quản lý tài nguyên chung
  • Thread-safe
  • Dễ dàng truy cập

Testing Patterns (Mẫu Testing) 🧪

1. Mock - Giả Lập Đối Tượng

type MockDatabase struct {
users map[string]*User
}

func (m *MockDatabase) GetUser(id string) (*User, error) {
if user, ok := m.users[id]; ok {
return user, nil
}
return nil, errors.New("không tìm thấy user")
}

💡 Giải thích:

  • Giả lập đối tượng
  • Dễ dàng test
  • Kiểm soát kết quả

2. Test Helper - Hàm Hỗ Trợ Test

func assertEqual(t *testing.T, got, want interface{}) {
t.Helper()
if got != want {
t.Errorf("nhận được %v, mong đợi %v", got, want)
}
}

💡 Giải thích:

  • Code test ngắn gọn
  • Dễ đọc
  • Tái sử dụng

Best Practices (Cách sử dụng tốt nhất) ✅

  1. Singleton

    // ✅ Đúng
    var instance *Singleton
    var once sync.Once

    // ❌ Sai
    var instance *Singleton
    // Không thread-safe
  2. Factory

    // ✅ Đúng
    func CreateProduct(productType string) Product {
    switch productType {
    case "A": return &ConcreteProductA{}
    case "B": return &ConcreteProductB{}
    default: return nil
    }
    }

    // ❌ Sai
    // Tạo đối tượng trực tiếp
  3. Observer

    // ✅ Đúng
    func (s *Subject) Notify(data interface{}) {
    for _, o := range s.observers {
    o.Update(data)
    }
    }

    // ❌ Sai
    // Không thông báo thay đổi
  4. Error Handling

    // ✅ Đúng
    func wrapError(err error, msg string) error {
    return fmt.Errorf("%s: %w", msg, err)
    }

    // ❌ Sai
    // Không bọc lỗi

Tiếp theo 🎯

Trong các bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về performance
  • Học cách tối ưu code
  • Khám phá các kỹ thuật nâng cao
  • Thực hành với các dự án thực tế

💡 Lời khuyên: Design patterns là công cụ mạnh mẽ, nhưng hãy sử dụng chúng một cách thông minh. Đừng áp dụng mẫu thiết kế khi không cần thiết!