我寫了一些創建“人類”的代碼。人類每 100 毫秒過一次生日,您可以像這樣訂閱事件:
pers1 := new(Human)
pers1.Init("John")
pers1.Subscribe(func(h Human) { fmt.Printf("Observer1 : %s", h.String()); return })
pers1.Subscribe(func(h Human) { fmt.Printf("Observer2 : %s", h.String()); return })
time.Sleep(3 * time.Second)
輸出如下
HUMAN John is born // by init
HUMAN John is now followed by 0x4901a0 // by subscribe
There is now 1 observers
HUMAN John is now followed by 0x490300 // by subscribe
There is now 2 observers
[T 0100ms]
HUMAN John has its birthday // after 100ms : birthday happens
Observer1 : HUMAN : John is 1 // callback
Observer2 : HUMAN : John is 1 // callback
// ... continue for 3 seconds
詳細代碼在這里,但問題不存在 https://goplay.tools/snippet/7qsZ1itcqrS
我的問題如下:
我想創建一個介面Producer對應于產生我可以訂閱的事件的事物。
您可以訂閱:
- 過生日的人
- 可以檢測濕度變化的濕度傳感器
- 收到郵件的郵件服務器...
在我的示例中,回呼函式作為引數:一個Human。年齡變了的那個……
以相同的方式,濕度傳感器的給定事件將期望傳感器結構。
我的問題是
- 我認為這樣做有什么意義嗎?(這是一個學者問題,沒有東西可以作業)
- 如果是,如何。我找不到相關的例子
那將是
type Producer interface{
Subscribe( func( < something variable >) )
}
我無法得到一些作業。我也很難為這個問題找到一個好的標題。隨意給我一個更好的。
uj5u.com熱心網友回復:
根據您的需要,這里有三個可能適合您的選項。
選項 1:已發布專案的通用介面
不僅為可以擁有訂閱者的發布者創建一個介面,還為那些發布者可以發布的東西創建一個介面:
type Item interface{
Description() string
Age() int
}
type human struct{
age int
}
func (h *human) Description() string {
return "human"
}
func (h *human) Age() int {
return h.age
}
type Publisher interface{
Subscribe(func(Item))
}
type humanProducer struct{
subscribers []func(Item)
}
func (hp *humanProducer) Subscribe(f func(Item) {
hp.subscribers = append(hp.subscribers, f)
}
// Example use
func addSubscriber(p Publisher, f func(Item)) {
p.Subscribe(f)
}
func main() {
hp := &humanProducer{}
addSubscriber(p, func(i Item) {
fmt.Printf("Got a %s that is %d years old.\n", i.Description(), i.Age())
})
}
您現在可以通過讓它們實作Item
介面來設定要發布的其他型別的事物。這里的Description
和Age
方法只是示例 - 您可以在其中添加任何您需要的方法。
優點
- 避免反射。
- 避免型別引數;適用于 Go 1.18 之前的版本。
- 訂閱者可以接收多種專案。
- 發布者可以發布多種專案。
缺點
- 已發布的專案不能只是任何東西——您必須定義一組預先確定的功能,所有型別的已發布專案都必須具備這些功能。
- 已發布的專案隱藏在界面后面,因此您只能使用
Item
界面中公開的功能,除非您開始投射或使用反射。
選項 2:使用型別引數的介面
將型別引數添加到介面本身:
type human struct{
age int
}
type Publisher[T any] interface{
Subscribe(func(T))
}
type humanProducer struct{
subscribers []func(*human)
}
func (hp *humanProducer) Subscribe(f func(*human) {
hp.subscribers = append(hp.subscribers, f)
}
// Example use
func addSubscriber[T any](p Publisher[T], f func(T)) {
p.Subscribe(f)
}
func main() {
hp := &humanProducer{}
addSubscriber[*human](p, func(h *human) {
fmt.Printf("Got a human that is %d years old.\n", h.age)
})
}
優點
- 避免反射。
- 對可以發布的內容沒有限制。
- 已發布的專案不會隱藏在界面后面。
缺點
- 發布者只能發布一種特定型別的專案。
- 訂閱者只能接收一種特定型別的專案。
- 任何使用
Publisher
介面都需要使用型別引數。僅適用于 Go 1.18 或更高版本。
選項 3:反射/鑄造
允許發布者發布任何內容并在訂閱者中使用反射或強制轉換來整理發布的內容型別:
type human struct{
age int
}
type Publisher interface{
Subscribe(func(any))
}
type humanProducer struct{
subscribers []func(any)
}
func (hp *humanProducer) Subscribe(f func(any) {
hp.subscribers = append(hp.subscribers, f)
}
// Example use
func addSubscriber(p Publisher, f func(any)) {
p.Subscribe(f)
}
func main() {
hp := &humanProducer{}
addSubscriber(p, func(i any) {
if h, ok := any.(*human); ok {
fmt.Printf("Got a human that is %d years old.\n", h.age)
}
})
}
如果使用 Go pre-1.18,請替換any
為interface{}
. 此選項與選項 1 類似,但Item
界面為空。
優點
- 避免型別引數;適用于 Go 1.18 之前的版本。
- 對可以發布的內容沒有限制。
- 已發布的專案不會隱藏在界面后面。
- 訂閱者可以接收多種專案。
- 發布者可以發布多種專案。
缺點
- 需要反射或投射,這是緩慢、笨拙且不太安全的。
- 訂閱者將不得不做額外的作業來弄清楚他們收到了什么樣的物品。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/520350.html
標籤:去