Like Share Discussion Bookmark Smile

J.J. Huang   2025-06-10   Getting Started Golang 07.套件   瀏覽次數:次   DMCA.com Protection Status

Go | 套件結構介面協同設計

💬 簡介

隨著程式規模擴大,設計良好的模組結構與職責劃分變得越來越重要。Go 雖然以簡潔著稱,但仍能藉由結構體與介面的設計,配合套件拆分,達到清楚的模組邊界與低耦合的維護策略。

本篇將介紹:

  • 結構體與介面的協同設計概念
  • 如何設計內聚的套件
  • 實現鬆耦合的模組分層架構

圖片來源:Gophers


🧩 結構體與介面協同設計的原則

  • 🧱 高內聚:功能集中且清晰

    每個套件應集中於單一領域邏輯,例如:
    • user 處理用戶邏輯
    • auth 處理驗證邏輯
    • store 處理資料存取
  • 🔗 低耦合:依賴抽象、隔離實作

    透過定義介面(interface),讓模組依賴行為定義而非具體實作。這樣可在不改動呼叫方邏輯的情況下替換實作內容。

📦 範例:使用者系統設計

  • 🛠️ 結構化的套件設計

    1
    2
    3
    4
    5
    6
    7
    8
    project/
    ├── main.go
    ├── user/
    │ ├── service.go
    │ └── model.go
    ├── store/
    │ ├── user_store.go
    │ └── memory_store.go
  • 📄 定義使用者結構體與介面

    user/model.go
    1
    2
    3
    4
    5
    6
    7
    package user

    type User struct {
    ID int
    Name string
    Email string
    }
    user/service.go
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package user

    type Store interface {
    GetByID(id int) (*User, error)
    Save(u *User) error
    }

    type Service struct {
    store Store
    }

    func NewService(s Store) *Service {
    return &Service{store: s}
    }

    func (s *Service) GetUser(id int) (*User, error) {
    return s.store.GetByID(id)
    }

    func (s *Service) CreateUser(u *User) error {
    return s.store.Save(u)
    }

    📝 使用介面 Store 隔離資料存取邏輯,Service 專注業務邏輯,符合高內聚。

  • 🗃️ 實作資料存取層

    store/user_store.go
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package store

    import (
    "errors"
    "project/user"
    )

    type InMemoryUserStore struct {
    data map[int]*user.User
    }

    func NewInMemoryUserStore() *InMemoryUserStore {
    return &InMemoryUserStore{data: make(map[int]*user.User)}
    }

    func (s *InMemoryUserStore) GetByID(id int) (*user.User, error) {
    u, ok := s.data[id]
    if !ok {
    return nil, errors.New("找不到用戶")
    }
    return u, nil
    }

    func (s *InMemoryUserStore) Save(u *user.User) error {
    s.data[u.ID] = u
    return nil
    }
  • 🚀 使用方式:main.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package main

    import (
    "fmt"
    "project/store"
    "project/user"
    )

    func main() {
    userStore := store.NewInMemoryUserStore()
    userService := user.NewService(userStore)

    newUser := &user.User{ID: 1, Name: "小明", Email: "ming@example.com"}
    _ = userService.CreateUser(newUser)

    u, err := userService.GetUser(1)
    if err != nil {
    fmt.Println("錯誤:", err)
    return
    }
    fmt.Printf("取得用戶:%+v\n", u)
    }

🧠 設計思維補充

原則 說明
Interface as contract 介面定義代表行為契約,有利於替換與測試
Dependency Injection 將實作注入(如 store),避免 Service 綁死具體實作
Package boundaries 模組依功能拆分(業務邏輯 / 資料存取 / 結構定義),保持清晰的職責劃分

🧪 測試與擴充性

由於使用介面注入,你可以很容易撰寫測試:

1
2
3
4
5
6
7
8
9
type fakeStore struct {}

func (f *fakeStore) GetByID(id int) (*user.User, error) {
return &user.User{ID: id, Name: "Test"}, nil
}

func (f *fakeStore) Save(u *user.User) error {
return nil
}

📝 單元測試可使用假實作來隔離依賴。


🎯 總結

將結構體與介面結合應用於套件設計,不但能保持程式邏輯清晰、可測、易維護,也有助於團隊協作與模組擴充。

本篇重點如下:

  • ✅ 使用介面隔離實作,促進低耦合設計
  • ✅ 套件應聚焦單一職責,促進高內聚
  • ✅ 配合結構化目錄與依賴注入,實現可擴充模組

最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。


註:以上參考了
Go