【问题标题】:Golang : create an interface to abstract a method which may have have variable argumentGolang:创建一个接口来抽象一个可能有可变参数的方法
【发布时间】:2022-10-24 10:38:52
【问题描述】:

我写了一些创建“人类”的代码。人类每 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

我的问题如下:

我想创建一个界面制片人对应于产生我可以订阅的事件的事物。

您可以订阅:

  • 过生日的人
  • 可以检测湿度变化的湿度传感器
  • 收到邮件的邮件服务器...

在我的示例中,回调函数作为参数:人类.年龄变了的那个……

以相同的方式,湿度传感器的给定事件将期望传感器结构。

我的问题是

  • 我认为这样做有道理吗? (这是一个学者问题,没有东西可以工作)
  • 如果是,如何。我找不到相关的例子

那将是

type Producer interface{ 
     Subscribe( func( < something variable >) )
}

我无法得到一些工作。 我也很难为这个问题找到一个好的标题。随意给我一个更好的。

【问题讨论】:

  • 您唯一可以使用的是any,这将迫使您进行丑陋的反思。或者:想出一个通用的接口。
  • Go 1.18+ 有泛型(也就是类型参数)。不会在Producer 接口类型帮助中添加类型参数吗?
  • 您可以使用带有类型参数的接口,但这是否有用取决于您想用它做什么。如果您只想抽象行为,则可以使用带有类型参数的结构

标签: go


【解决方案1】:

根据您的需要,这里有三个可能适合您的选项。


选项 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.
", i.Description(), i.Age())
  })
}

您现在可以通过让它们实现Item 接口来设置要发布的其他类型的事物。这里的DescriptionAge 方法只是示例——你可以在其中添加任何你需要的方法。

优点

  • 避免反射。
  • 避免类型参数;适用于 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.
", 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.
", h.age)
    }
  })
}

如果使用 Go pre-1.18,请将 any 替换为 interface{}。此选项与选项 1 类似,但有一个空的Item 接口。

优点

  • 避免类型参数;适用于 Go 1.18 之前的版本。
  • 对可以发布的内容没有限制。
  • 已发布的项目不会隐藏在界面后面。
  • 订阅者可以接收多种项目。
  • 发布者可以发布多种项目。

缺点

  • 需要反射或投射,这很慢、笨拙且不太安全。
  • 订阅者必须做额外的工作才能弄清楚他们收到了什么样的物品。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-10
    • 2011-11-28
    • 2016-01-20
    • 2018-04-20
    • 1970-01-01
    • 1970-01-01
    • 2012-12-25
    • 1970-01-01
    相关资源
    最近更新 更多