iT邦幫忙

3

什麼是高內聚與低耦合的程式架構?

  • 分享至 

  • xImage
  •  

今天想來分享高內聚 (High Cohesion) 與低耦合 (Loose Coupling) 的概念

🔹高內聚

定義:一個模組(或類別)裡的功能彼此高度相關、有一致性。

目標:讓每個模組專注做一件事,功能明確。

優點:易於理解、測試和維護。

🔹低耦合

定義:模組彼此之間依賴程度低,不會直接控制對方的行為。

目標:讓一個模組變更時,其他模組不用或很少跟著改變。

優點:彈性高,可替換性強,容易單元測試。

我們會希望程式架構朝著高內聚與低耦合的架構前進,但實際上在實作過程中,則是會出現三個版本。

  • 高內聚,高耦合

  • 低內聚,低耦合

  • 高內聚,低耦合

現在我們用使用者註冊功能這個需求,用 Go 實作三個版本的範例,來幫助大家理解,這三個版本,應該會長成什麼樣子。

🔹版本一、高內聚,高耦合

type UserService struct{}

func (s *UserService) Register(email, password string) error {
    // 驗證 email
    if !strings.Contains(email, "@") {
        return errors.New("invalid email")
    }

    // 模擬儲存
    fmt.Println("Saved:", email)

    // 模擬寄送歡迎信
    fmt.Println("Sent welcome email to:", email)

    return nil
}

func main() {
    service := &UserService{}
    err := service.Register("test@example.com", "123456")
    if err != nil {
        fmt.Println("Register failed:", err)
    } else {
        fmt.Println("Register success!")
    }
}

這個版本應該是每個軟體工程師剛開始接觸程式時會寫的版本,或是 PoC 版本為了實現功能的可行性。

優點

  • 業務流程集中在 UserService,邏輯一目了然(高內聚)

  • 初期開發快速直觀,容易看懂

  • 不需要額外抽象、interface,程式碼少

缺點

  • 直接綁定底層技術(DB、SMTP),導致耦合高

  • 單元測試會比較複雜

  • 變更其中某一個流程(如改寄信服務)時要改核心邏輯

  • 無法重用或替換元件

🔹版本二、低內聚,低耦合

type Validator struct{}
func (v *Validator) Validate(email string) error {
    if !strings.Contains(email, "@") {
        return errors.New("invalid email")
    }
    return nil
}

type DBWriter struct{}
func (d *DBWriter) Save(email, password string) error {
    // 模擬儲存
    fmt.Println("Saved:", email)
    return nil
}

type EmailNotifier struct{}
func (n *EmailNotifier) Send(email string) error {
    // 模擬寄送歡迎信
    fmt.Println("Sent welcome email to:", email)
    return nil
}

// 外部流程直接串接各個元件
func RegisterUserFlow(email, password string) error {
    validator := &Validator{}
    dbWriter := &DBWriter{}
    notifier := &EmailNotifier{}

    if err := validator.Validate(email); err != nil {
        return err
    }
    if err := dbWriter.Save(email, password); err != nil {
        return err
    }
    return notifier.Send(email)
}

func main() {
    err := RegisterUserFlow("test@example.com", "123456")
    if err != nil {
        fmt.Println("Register failed:", err)
    } else {
        fmt.Println("Register success!")
    }
}

這個版本比上一個版本更進階,我們試圖想要減少功能的耦合性,但卻可能造成業務邏輯的分散。

優點

  • 各模組單一責任,容易替換與測試(低耦合)

  • 無硬綁定底層邏輯,可抽換不同實作(如 mock / stub)

  • 方便獨立維護與重複使用元件

缺點

  • 業務流程邏輯分散在 main() 或 controller(低內聚)

  • 沒有中心協調者,不利於維護流程變化

  • 每次要新增流程(如 log、驗證碼)都要改外部流程呼叫

  • 難以重用整體註冊邏輯

🔹版本三、高內聚,低耦合

type EmailSender interface {
    Send(email string) error
}

type UserRepo interface {
    Save(email, password string) error
}

type Validator interface {
    Validate(email string) error
}

// EmailSender 實作
type SimulateEmailSender struct{}
func (s *SimulateEmailSender) Send(email string) error {
    fmt.Println("Send welcome email to:", email)
    return nil
}

// UserRepo 實作
type SimulateUserRepo struct{}
func (r *SimulateUserRepo) Save(email, password string) error {
    fmt.Println("Saved user to DB:", email)
    return nil
}

// Validator 實作
type SimpleValidator struct{}
func (v *SimpleValidator) Validate(email string) error {
    if !strings.Contains(email, "@") {
        return errors.New("invalid email format")
    }
    return nil
}

type UserService struct {
    validator Validator
    repo      UserRepo
    sender    EmailSender
}

func (s *UserService) Register(email, password string) error {
    if err := s.validator.Validate(email); err != nil {
        return err
    }
    if err := s.repo.Save(email, password); err != nil {
        return err
    }
    return s.sender.Send(email)
}

func main() {
    service := &UserService{
        validator: &SimpleValidator{},
        repo:      &SimulateUserRepo{},
        sender:    &SimulateEmailSender{},
    }

    err := service.Register("test@example.com", "123456")
    if err != nil {
        fmt.Println("Register failed:", err)
    } else {
        fmt.Println("Register success!")
    }
}

這個版本通常已經是維護到中大型專案時,很常見的程式架構。

優點

  • 業務邏輯集中在 UserService(高內聚)

  • 所有依賴透過 interface 傳入,方便替換 / 測試(低耦合)

  • 新增流程只需修改 UserService,維護成本低

  • 測試時可 mock interface,單元測試容易寫

  • 更符合 Clean Architecture、SOLID 原則

缺點

  • 初期設定較多,需要定義多個 interface 和實作

  • 對小型專案來說略顯複雜(但對中大型專案是必要的)

這篇文章摘自個人每週電子報的技術分享部分,歡迎關注與交流,也希望能帶給大家一些啟發。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言