减少 Golang 中的代码重复

Reducing code duplication in Golang

我找不到 "go-way" 来解决代码重复问题。这就是问题所在。考虑以下因素:

type (
  WithKey interface {
    key() string
  }

  SharedFunctionality interface {
    WithKey
    MethodA() string
    MethodB() string
    // ... etc ...
  }

  FirstType struct { ... }
  SecondType struct { ... }
  // ... etc ...
)
func (ft *FirstType) key() string { ... }
func (st *SecondType) key() string { ... }

现在,SharedFunctionality 中的方法只依赖于 key() 方法的结果。我可以像下面这样实现它们:

func runMethodA(k WithKey) string {
  key := k.key()
  // do something and return a string
}
func runMethodB(k WithKey) string {
  key := k.key()
  // do something else and return a string
}

func (ft *FirstType) MethodA() string { return runMethodA(ft) }
func (ft *FirstType) MethodB() string { return runMethodB(ft) }
func (st *SecondType) MethodA() string { return runMethodA(st) }
func (st *SecondType) MethodB() string { return runMethodB(st) }

我不喜欢这种方法的地方在于,当我添加更多类型(ThirdType、FourthType 等)或向 SharedFunctionality 添加更多方法时,我必须添加大量样板代码...特别是对于 M SharedFunctionality 中的方法和 N 类型,我将不得不像上面的 4 个一样拼出 M*N 个单行代码。

喜欢做的是:

func (k WithKey) MethodA() string {
  key := k.key()
  // do something
}

换句话说:我喜欢在接口类型上定义一个方法。含义:所有实现 "WithKey" 的对象将自动获得 MethodA() stringMethodB() string 等,因此它们将自动实现 SharedFunctionality 接口。 Java接口.

中的默认方法

但是,我知道在接口类型中定义方法是不可能的...

解决这个问题的方法是什么?

我见过一种方法,我会创建一个带有接口类型的匿名字段的结构,然后在其中实现方法:

type SharedFuncStruct struct {
  WithKey
}
func (sfs *SharedFuncStruct) MethodA() string {
  key := sfs.key()
  // whatever
}
// same for MethodB()

然后要使用它,我会做类似的事情:

first := ... getFirstTypeValue()
sfs := &SharedFuncStruct{first}
sfs.MethodA() // etc

这看起来可行,但感觉样板代码太多了。

还有其他选择吗?

在我看来你需要提取一个包。我拥有该功能的方式是

package keyed

type hasKey interface {
    Key() string
}

func MethodA(k hasKey) string {
    key := k.Key()
    // whatever
}

func MethodB(k hasKey) string {
    key := k.Key()
    // whatever
}

然后

package your_package

import "keyed"

type (
    FirstType struct { ... }
    SecondType struct { ... }
)

func (ft *FirstType) Key() string { ... }
func (st *SecondType) Key() string { ... }

func main() {
    first := &FirstType{}
    second := &SecondType{}
    keyed.MethodA(first)
    keyed.MethodA(second)
    keyed.MethodB(first)
    keyed.MethodB(second)
}

有趣的事实:您可以将接口嵌入到结构中,然后结构会自动实现该接口。您可以使用它来有效地在接口上定义方法:

https://play.golang.org/p/ZufTOzr9ig

type (
    WithKey interface {
        key() string
    }

    SharedFunctionality interface {
        WithKey
        MethodA() string
        MethodB() string
    }

    KeyHolder struct {
        WithKey
    }

    FirstType struct { ... }
    SecondType struct { ... }
)

func (k *KeyHolder) MethodA() string {
    key := k.key()
    // ...
}

func (k *KeyHolder) MethodB() string {
    key := k.key()
    // ...
}

func NewSharedFunctionality(w WithKey) SharedFunctionality {
    return &KeyHolder{w}
}

func (ft *FirstType) key() string { ... }
func (st *SecondType) key() string { ... }

在这种情况下,KeyHolder 结构嵌入了 WithKey 接口,因此可以容纳任何具有 key() string 方法(FirstTypeSecondType 有)。然后,您可以在该结构上定义 MethodAMethodB,然后该结构将满足 WithKey 接口(因为它嵌入了它)和 SharedFunctionality 接口,使用任何密钥由嵌入的 WithKey.

返回

换句话说,不是将 FirstType 包装在 WithKey 中,然后包装在 SharedFunctionality 中(意味着 FirstType 本身必须定义 key()MethodA(),和 MethodB()),你将 FirstType 包装在 WithKey 中,然后将它嵌入(作为 WithKey 接口)在一些其他结构中,这些结构仅用于定义那些默认值方法 MethodAMethodB,然后实现 SharedFunctionality 接口。