英文:
Reducing code duplication in Golang
问题
我在寻找解决代码重复问题的"Go方式"时遇到了困难。以下是问题的描述:
type (
WithKey interface {
key() string
}
SharedFunctionality interface {
WithKey
MethodA() string
MethodB() string
// ... 其他方法 ...
}
FirstType struct { ... }
SecondType struct { ... }
// ... 其他类型 ...
)
func (ft *FirstType) key() string { ... }
func (st *SecondType) key() string { ... }
在SharedFunctionality
中的方法只依赖于key()
方法的结果。我可以像下面这样实现它们:
func runMethodA(k WithKey) string {
key := k.key()
// 做一些操作并返回一个字符串
}
func runMethodB(k WithKey) string {
key := k.key()
// 做一些其他操作并返回一个字符串
}
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
添加更多方法,我不得不添加大量样板代码...具体来说,对于SharedFunctionality
中的M个方法和N个类型,我需要写出M*N个类似上面的一行代码。
我希望能够做到像下面这样:
func (k WithKey) MethodA() string {
key := k.key()
// 做一些操作
}
换句话说,我希望在接口类型上定义一个方法。也就是说,所有实现了"WithKey"的对象都会自动获得MethodA() string
、MethodB() string
等方法,从而自动实现SharedFunctionality
接口。类似于Java接口中的默认方法。
然而,我知道在接口类型中定义方法是不可能的...
有没有解决这个问题的"Go方式"呢?
我看到过一种方法,可以创建一个带有接口类型匿名字段的结构体,然后在其中实现方法:
type SharedFuncStruct struct {
WithKey
}
func (sfs *SharedFuncStruct) MethodA() string {
key := sfs.key()
// 其他操作
}
// MethodB()也是类似的实现
然后使用它时,可以这样做:
```go
first := ... 获取FirstType的值()
sfs := &SharedFuncStruct{first}
sfs.MethodA() // 等等
这看起来似乎可以工作,但仍然感觉有太多的样板代码。
还有其他的替代方案吗?
英文:
I'm having trouble find the "go-way" to solve a code duplication issue. Here's the problem. Consider the following:
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 { ... }
Now, the methods in SharedFunctionality
they only depend on the results of the key()
method. I could implement them like the following:
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) }
What I dislike about this approach is that, as I add more types (ThirdType, FourthType, etc) or as I add more methods to SharedFunctionality, I have to add tons of boilerplate code... specifically, for M methods in SharedFunctionality, and N types, I would have to spell out M*N one-liners like the 4 above.
What I would love to do is something like:
func (k WithKey) MethodA() string {
key := k.key()
// do something
}
In other words: I'd love to define a method on an Interface type. Meaning: all objects that implement "WithKey" would automatically get MethodA() string
, MethodB() string
, etc, therefore they would automatically implement the SharedFunctionality
interface. Something like default methods in Java interfaces.
However, I know it's impossible to define a method in an Interface type...
What's the go-way of solving this problem?
I've seen an approach where I would create a struct with an anonymous field of the interface type, and then implement the methods there:
type SharedFuncStruct struct {
WithKey
}
func (sfs *SharedFuncStruct) MethodA() string {
key := sfs.key()
// whatever
}
// same for MethodB()
Then to use it, I would do something like:
first := ... getFirstTypeValue()
sfs := &SharedFuncStruct{first}
sfs.MethodA() // etc
This looks like it could work, but it still feels like too much boilerplate code.
Any other alternatives?
答案1
得分: 2
看起来你需要提取一个包。我会按照以下方式编写函数:
package keyed
type hasKey interface {
Key() string
}
func MethodA(k hasKey) string {
key := k.Key()
// 其他操作
}
func MethodB(k hasKey) string {
key := k.Key()
// 其他操作
}
然后在你的包中:
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)
}
希望这对你有帮助!
英文:
It looks to me like you need to extract a package. The way I would have the function is
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
}
and then
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)
}
答案2
得分: 1
有趣的事实:你可以将接口嵌入到结构体中,然后结构体会自动实现该接口。你可以利用这一点,在接口上有效地定义方法:
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
方法的对象(FirstType
和 SecondType
都有该方法)。然后,你可以在该结构体上定义 MethodA
和 MethodB
,该结构体将同时实现 WithKey
接口(因为它嵌入了该接口)和 SharedFunctionality
接口,使用嵌入的 WithKey
返回的任何键。
换句话说,不需要将 FirstType
包装在 WithKey
中,然后再包装在 SharedFunctionality
中(这意味着 FirstType
本身必须定义 key()
、MethodA()
和 MethodB()
),而是将 FirstType
包装在 WithKey
中,然后将其嵌入(作为 WithKey
接口)在另一个结构体中,该结构体仅用于定义默认方法 MethodA
和 MethodB
,从而实现 SharedFunctionality
接口。
英文:
Fun fact: you can embed interfaces into structs, and the struct then automatically fulfills that interface. You can use this to effectively define methods on interfaces:
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 { ... }
In this case, the KeyHolder
struct embeds the WithKey
interface, and thus can hold anything that has the key() string
method (which both FirstType
and SecondType
have). You can then define MethodA
and MethodB
on that struct, and that struct will then fulfill both the WithKey
interface (because it embeds it) and the SharedFunctionality
interface, using whatever key is returned by the embedded WithKey
.
In other words, instead of wrapping FirstType
in WithKey
and then in SharedFunctionality
(meaning FirstType
itself has to define key()
, MethodA()
, and MethodB()
), you wrap FirstType
in WithKey
, then embed it (as a WithKey
interface) in some other structure that exists solely to define those default methods MethodA
and MethodB
, which then fulfills the SharedFunctionality
interface.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论