Like Share Discussion Bookmark Smile

J.J. Huang   2025-03-23   Getting Started Golang 05.結構   瀏覽次數:次   DMCA.com Protection Status

Go | 方法與事件的統一呼叫

💬 簡介

在 Go 語言中,方法和函式的處理方式具有相似性,使得兩者在使用上可以互換。這種設計簡化了方法與函式在作為回呼函式時的混用,開發者無需過多關心是呼叫的是普通函式還是結構方法。這種特性對於構建如事件系統等場景具有極大便利。

本文將詳細介紹 Go 語言如何統一呼叫方法與函式,並以此為基礎實現一個簡單的事件系統。

圖片來源:Gophers


🔍 方法與函式的統一呼叫

在 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
28
29
30
31
32
33
package main

import "fmt"

// 定義一個結構
type class struct {
}

// 為結構添加方法
func (c *class) Do(v int) {
fmt.Println("call method do:", v)
}

// 普通函式
func funcDo(v int) {
fmt.Println("call function do:", v)
}

func main() {
// 聲明一個回呼變數
var delegate func(int)

// 創建結構實例
c := new(class)

// 設置回呼為方法
delegate = c.Do
delegate(100) // 輸出: call method do: 100

// 設置回呼為普通函式
delegate = funcDo
delegate(100) // 輸出: call function do: 100
}

📝 在這個範例中,我們將 classDo 方法和普通函式 funcDo 都賦值給同一個 delegate 函式變數,並且能夠根據賦值自動呼叫相應的函式或方法。


🛠 事件系統的實現

利用 Go 的這一特性,我們可以實現一個簡單的事件系統。事件系統的主要目的是解耦事件的觸發與事件的處理者,從而讓系統更靈活、可擴展。

📌 事件註冊

事件註冊是將事件名稱與相應的回呼函式綁定在一起,當事件發生時,系統會根據事件名稱呼叫對應的回呼函式。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

// 事件列表,通過事件名稱來查詢回呼函式
var eventByName = make(map[string][]func(interface{}))

// 註冊事件
func RegisterEvent(name string, callback func(interface{})) {
list := eventByName[name]
list = append(list, callback)
eventByName[name] = list
}

📝 在這個範例中,RegisterEvent 函式將事件名稱與回呼函式綁定在一起,將回呼函式保存到 eventByName 地圖中,這樣當事件發生時可以根據事件名稱來查詢並呼叫回呼函式。

📌 事件呼叫

當事件發生時,系統會呼叫與事件名稱對應的所有回呼函式,並將事件參數傳遞給它們。

1
2
3
4
5
6
7
// 呼叫事件
func CallEvent(name string, param interface{}) {
list := eventByName[name]
for _, callback := range list {
callback(param)
}
}

📝 CallEvent 函式會根據事件名稱查詢回呼函式列表,然後逐一呼叫這些回呼函式並將事件參數傳遞給它們。


🧰 使用事件系統

使用事件系統可以將事件發起方與事件處理方解耦。以下範例展示了如何使用事件系統註冊和呼叫事件。

  • 範例:註冊與呼叫事件
    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 main

    import "fmt"

    // 定義角色結構
    type Actor struct {}

    // 角色的事件處理函式
    func (a *Actor) OnEvent(param interface{}) {
    fmt.Println("actor event:", param)
    }

    // 全局事件處理函式
    func GlobalEvent(param interface{}) {
    fmt.Println("global event:", param)
    }

    func main() {
    a := new(Actor)

    // 註冊事件
    RegisterEvent("OnSkill", a.OnEvent)
    RegisterEvent("OnSkill", GlobalEvent)

    // 呼叫事件
    CallEvent("OnSkill", 100) // 輸出: actor event: 100 global event: 100
    }

    📝 在這個範例中,我們註冊了兩個回呼函式來處理 OnSkill 事件,這些回呼函式分別來自角色的 OnEvent 方法和全局的 GlobalEvent 函式。當事件被呼叫時,這些回呼函式會按照註冊順序被執行。


⚠️ 注意事項

  • 事件回呼順序
    • 事件系統並不保證回呼函式的呼叫順序,這意味著不同的回呼函式可能會按照不同的順序執行。如果需要確保回呼順序,可以自行管理回呼的執行順序。
  • 重複註冊
    • 對同一事件註冊多個回呼函式時,它們會被按順序執行,不會互相覆蓋。

🎯 總結

  • Go 語言允許方法和函式具有相同的簽名,因此可以將它們視為統一的概念來處理。
  • 基於這一特性,我們可以輕鬆實現事件系統,將事件的發起與處理解耦,並在需要時動態註冊和呼叫事件回呼。
  • 雖然事件系統提供了極大的靈活性,但需要注意回呼函式的順序執行問題,以及如何處理多次註冊相同事件的情況。

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


註:以上參考了
Go