Go語言可以將型別的方法與普通函數視為一個概念,從而簡化方法和函數混合作為回撥型別時的複雜性。這個特性和 C# 中的代理(delegate)類似,呼叫者無須關心誰來支援呼叫,系統會自動處理是否呼叫普通函數或型別的方法。
本節中,首先將用簡單的例子了解 Go語言是如何將方法與函數視為一個概念,接著會實現一個事件系統,事件系統能有效地將事件觸發與響應兩端程式碼解耦。
方法和函數的統一呼叫
本節的例子將讓一個結構體的方法(class.Do)的引數和一個普通函數(funcDo)的引數完全一致,也就是方法與函數的簽名一致。然後使用與它們簽名一致的函數變數(delegate)分別賦值方法與函數,接著呼叫它們,觀察實際效果。詳細實現請參考如下程式碼。
package main
import "fmt"
// 宣告一個結構體
type class struct {
}
// 給結構體新增Do方法
func (c *class) Do(v int) {
fmt.Println("call method do:", v)
}
// 普通函數的Do
func funcDo(v int) {
fmt.Println("call function do:", v)
}
func main() {
// 宣告一個函數回撥
var delegate func(int)
// 建立結構體範例
c := new(class)
// 將回撥設為c的Do方法
delegate = c.Do
// 呼叫
delegate(100)
// 將回撥設為普通函數
delegate = funcDo
// 呼叫
delegate(100)
}
程式碼說明如下:
-
第 10 行,為結構體新增一個 Do() 方法,引數為整型。這個方法的功能是列印提示和輸入的引數值。
-
第 16 行,宣告一個普通函數,引數也是整型,功能是列印提示和輸入的引數值。
-
第 24 行,宣告一個 delegate 的變數,型別為 func(int),與 funcDo 和 class 的 Do() 方法的引數一致。
-
第 30 行,將 c.Do 作為值賦給 delegate 變數。
-
第 33 行,呼叫 delegate() 函數,傳入 100 的引數。此時會呼叫 c 範例的 Do() 方法。
-
第 36 行,將 funcDo 賦值給 delegate。
-
第 39 行,呼叫 delegate(),傳入 100 的引數。此時會呼叫 funcDo() 方法。
執行程式碼,輸出如下:
call method do: 100
call function do: 100
這段程式碼能執行的基礎在於:無論是普通函數還是結構體的方法,只要它們的簽名一致,與它們簽名一致的函數變數就可以儲存普通函數或是結構體方法。了解了 Go語言的這一特性後,我們就可以將這個特性用在事件中。
事件系統基本原理
事件系統可以將事件派發者與事件處理者解禍。例如,網路底層可以生成各種事件,在網路連線上後,網路底層只需將事件派發出去,而不需要關心到底哪些程式碼來響應連線上的邏輯。或者再比如,你註冊、關注或者訂閱某“大V”的社交訊息後,“大V”發生的任何事件都會通知你,但他並不用了解粉絲們是如何為她喝彩或者瘋狂的。如下圖所示為事件系統基本原理圖。
圖:事件系統基本原理