减少Golang中的代码重复

减少Golang中的代码重复,go,interface,refactoring,code-duplication,Go,Interface,Refactoring,Code Duplication,我很难找到解决代码重复问题的方法。问题出在这里。考虑以下事项: type ( WithKey interface { key() string } SharedFunctionality interface { WithKey MethodA() string MethodB() string // ... etc ... } FirstType struct { ... } SecondType struct { ... }

我很难找到解决代码重复问题的方法。问题出在这里。考虑以下事项:

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 { ... }
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) }
现在,
SharedFunctionality
中的方法只依赖于
key()
方法的结果。我可以像下面这样实现它们:

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 { ... }
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等)或向SharedFunction添加更多方法时,我必须添加大量样板代码。。。具体地说,对于SharedFunctionality中的M个方法和N个类型,我必须像上面的4个一样写出M*N个一行程序

我想做的是:

func (k WithKey) MethodA() string {
  key := k.key()
  // do something
}
first := ... getFirstTypeValue()
sfs := &SharedFuncStruct{first}
sfs.MethodA() // etc
换句话说:我想在接口类型上定义一个方法。含义:所有实现“WithKey”的对象将自动获得
MethodA()字符串
MethodB()字符串
,等等,因此它们将自动实现
共享功能
接口。类似于Java接口中的默认方法

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

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

我看到了一种方法,在这种方法中,我将使用接口类型的匿名字段创建一个结构,然后在其中实现方法:

type SharedFuncStruct struct {
  WithKey
}
func (sfs *SharedFuncStruct) MethodA() string {
  key := sfs.key()
  // whatever
}
// same for MethodB()
然后,要使用它,我会做如下操作:

func (k WithKey) MethodA() string {
  key := k.key()
  // do something
}
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)
}

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

在这种情况下,
KeyHolder
struct嵌入了
WithKey
接口,因此可以保存任何具有
key()字符串
方法的内容(这两种方法都是
FirstType
SecondType
所具有的)。然后可以在该结构上定义
MethodA
MethodB
,然后该结构将使用嵌入的
WithKey
返回的任何键来实现
WithKey
接口(因为它嵌入了它)和
SharedFunction
接口


换句话说,与用key在
中包装
FirstType
,然后在
SharedFunctionality
中包装
FirstType
不同(意思是
FirstType
本身必须定义
key()
MethodA()
MethodB()
),您可以用key
FirstType
包装在
中,然后嵌入它(作为带有按键的
界面)在另一个结构中,它仅用于定义默认方法
MethodA
MethodB
,然后实现
SharedFunctionality
接口。

您已经大致概述了您的选项。请参见下面dave的答案。并思考一下:如果一个具体类型已经有了会发生什么de>MethodA()
method除了
key()
?至少嵌入使得
MethodA()
的含义(在“最浅的深度”)更加明确。您的问题本质上是“如何在go中实现继承和虚拟方法?”,但我认为你可能有一个xy问题。如果你描述一下你正在做的事情的背景,可能有一个解决方案不是基于java风格的面向对象设计,它更适合go。我喜欢这种方法。我仍然觉得我必须键入比我预期的更多的内容(即,在功能上执行
新闻共享)(firstTypeInstance)
因此我可以调用我的
.MethodA()
),但这似乎是我能得到的最好的方法。我也喜欢Dave的方法,但至少现在,这个解决方案似乎对我更有效(即,让一个对象实现这些方法,而不必调用包上的函数)。谢谢!谢谢!这肯定会减少样板代码的数量。我当然会考虑为某些场景创建包。对于我目前正在处理的特定场景,Kaedys的建议似乎更好一些——我接受他的解决方案;但也感谢您的解决方案,非常优雅和有用。+1!