【问题标题】:Sharing method implementations between different structs在不同结构之间共享方法实现
【发布时间】:2021-07-23 20:47:33
【问题描述】:

假设我们有 2 个结构共享一个名称和用途相同但大小不同的属性:

type (
    L16 struct {
        Length uint16
    }

    L32 struct {
        Length uint32
    }
)

目标是使这些结构具有GetLength 方法,具有完全相同的签名和实现:

func (h *L16) GetLength() int {
    return int(h.Length)
}

func (h *L32) GetLength() int {
    return int(h.Length)
}

——但要避免对每个结构重复实现。

所以我试试:

type (

    LengthHolder interface {
        GetLength() int
    }

    LengthHolderStruct struct {
        LengthHolder
    }

    L16 struct {
        LengthHolderStruct
        Length uint16
    }

    L32 struct {
        LengthHolderStruct
        Length uint32
    }

)

func (h *LengthHolderStruct) GetLength() int {
    return int(h.Length)
}

——但是 h.Length undefined (type *LengthHolderStruct has no field or method Length) 的错误。

我们怎么做?

【问题讨论】:

  • @MrFuppes 在我的真实用例中,还有更多的结构可以使用GetLength 方法。每个结构重复它需要大量的维护开销。
  • @mh-cbon 为什么需要只有一个 Length 属性的结构?我真的认为在实际用例中这些结构会有更多字段是显而易见的

标签: go dry composition go-interface


【解决方案1】:

不客气的回答是你不能你不应该。只需在每个结构上实现该方法,让您和其他维护者的未来都开心。

无论如何,假设你绝对必须这样做,当然 embedded 类型对 embedding 类型一无所知,所以你不能引用Length来自LengthHolderStruct

就个人而言,我认为@mh-cbon answer 是一个不错的折衷方案。为了提供替代方案,您可以通过在嵌入式结构上将 Length 字段声明为 interface{} 并使用类型开关(在 bin )。

我不会在我的生产系统中使用以下代码,但你可以这样:

func main() {
    l16 := L16{
        LengthHolderStruct: LengthHolderStruct{
            Length: uint16(200), 
            // but nothing stops you from setting uint32(200)
        },
    }
    fmt.Println(l16.GetLength())
}

type (
    LengthHolder interface {
        GetLength() int
    }

    LengthHolderStruct struct {
        Length interface{}
    }

    L16 struct {
        LengthHolderStruct
    }

    L32 struct {
        LengthHolderStruct
    }
)

func (h *LengthHolderStruct) GetLength() int {
    switch t := h.Length.(type) {
    case uint16:
        return int(t)
    case uint32:
        return int(t)
    }
    return 0
}

一旦语言得到类型参数,你的问题就会有不同的答案:

type Constraint interface {
     type uint16, uint32
     // or `~uint16 | ~uint32` with type sets
}

type LX[T Constraint] struct {
    Length T
}

func (h *LX[T]) GetLength() int {
    return int(h.Length)
}

func main() {
    lx := LX[uint16]{
        Length: uint16(200),
    }
    fmt.Println(lx.GetLength()) // 200
}

Go2 游乐场:https://go2goplay.golang.org/p/nDZxPlXhP6H

【讨论】:

  • 感谢您的 go2 演示。这确实是我们想要的。
  • 所以,底线是我应该只为每个结构重复方法实现,即使我有十几个结构。真的吗?
  • @Greendrake 如果重复对你来说是个大问题,你可以用//go:generate自动化其中的一些问题
  • 很高兴知道,谢谢。听起来我应该回到 PHP :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多