英文:
How to keep code DRY in Golang
问题
如何在Go中避免重复代码?
type Animal interface {
Kingdom() string
Phylum() string
Family() string
}
type Wolf struct {}
type Tiger struct {}
func (w Wolf) Kingdom() string {return "Animalia"}
func (w Wolf) Phylum() string {return "Chordata"}
func (w Wolf) Family() string {return "Canidae"}
我为Wolf
类型实现了三个方法,并且我需要为Tiger
类型实现所有的方法来满足接口要求。但是Kingdom
和Phylum
方法对于两种类型来说是相同的。有没有办法只为Tiger
类型实现Family
方法而不需要为每种类型重复实现所有三个方法呢?
func (t Tiger) Family() string {return "Felidae"}
而不需要为每种类型重复实现所有三个方法吗?
英文:
EDIT++:
How to not to repeat my code in Go?
type Animal interface {
Kingdom() string
Phylum() string
Family() string
}
type Wolf struct {}
type Tiger struct {}
func (w Wolf) Kingdom() string {return "Animalia"}
func (w Wolf) Phylum() string {return "Chordata"}
func (w Wolf) Family() string {return "Canidae"}
I implemented a three methods for Wolf
type and I need to implement all the methods for Tiger
type to implement the interface. But Kingdom
and Phylum
methods are the same for both types. Is it somehow possible to implement only Family
method for Tiger
type:
func (t Tiger) Family() string {return "Felidae"}
and not to repeat all the three methods for each type?
Disclaimer
Please don't be confused with simple string returns in the methods, in a real case I need different method implementations not just pre-defined values. Using this silly style I want to avoid of defiling your brains. So skip methods at all is not the way. Thanks
答案1
得分: 15
这是一个经典的组合:
type Wolf struct {
Animalia
Chordata
Canidae
}
type Tiger struct {
Animalia
Chordata
Felidae
}
type Animalia struct{}
func (Animalia) Kingdom() string { return "动物界" }
type Chordata struct{}
func (Chordata) Phylum() string { return "脊索动物门" }
type Canidae struct{}
func (Canidae) Family() string { return "犬科" }
type Felidae struct{}
func (Felidae) Family() string { return "猫科" }
func main() {
w := Wolf{}
t := Tiger{}
fmt.Println(w.Kingdom(), w.Phylum(), w.Family())
fmt.Println(t.Kingdom(), t.Phylum(), t.Family())
}
Playground: https://play.golang.org/p/Jp22N2IuHL.
英文:
This is classical composition:
type Wolf struct {
Animalia
Chordata
Canidae
}
type Tiger struct {
Animalia
Chordata
Felidae
}
type Animalia struct{}
func (Animalia) Kingdom() string { return "Animalia" }
type Chordata struct{}
func (Chordata) Phylum() string { return "Chordata" }
type Canidae struct{}
func (Canidae) Family() string { return "Canidae" }
type Felidae struct{}
func (Felidae) Family() string { return "Felidae" }
func main() {
w := Wolf{}
t := Tiger{}
fmt.Println(w.Kingdom(), w.Phylum(), w.Family())
fmt.Println(t.Kingdom(), t.Phylum(), t.Family())
}
Playground: https://play.golang.org/p/Jp22N2IuHL.
答案2
得分: 4
这看起来非常像对接口的误用。接口并不是类的替代品;它们是类型可以做的表达方式。你这里存储的是数据。应该使用结构体来存储数据。
type Animal struct {
kingdom string
phylum string
family string
}
var wolf = Animal{"Animalia", "Chordata", "Canidae"}
var tiger = wolf
tiger.family = "Felidae"
英文:
This looks very much like an misuse of interfaces. Interfaces are not a replacement for classes; they're an expression of what a type can do. What you have here is data. Store data in structs.
type Animal struct {
kingdom string
phylum string
family string
}
var wolf = Animal{"Animalia", "Chordata", "Canidae"}
var tiger = wolf
tiger.family = "Felidae"
答案3
得分: 3
由于我对这个功能很感兴趣,我已经阅读了一些关于这个主题的文章,并将其整理成了几个参考点。
##嵌入
这个功能被称为"嵌入"。它解决了重复方法实现的问题。基本语法如下:
type Person struct {
Name string
}
type Speaker struct { // Speaker是一个抽象概念,它没有名字
*Person // 这里有一个没有字段名的类型。它是匿名的。
}
##包装和装饰
是的,虽然没有面向对象编程(OOP),但代码仍然必须保持DRY(Don't Repeat Yourself)。最清晰的思考这个功能的方式是将其视为用方法包装结构体。因此,描述匿名字段最准确的方式是"装饰器"模式(对于Python程序员来说是众所周知的)。
func (a *Speaker) Introduce(){ // 但是Speaker可以自我介绍
fmt.Println(a.Name) // 我们可以直接访问被包装结构体的属性。
}
##组合和重写
我们还可以组合在结构体上实现的方法。
func (s Speaker) Speak() string {
return "Blah-blah"
}
type Den struct { // 在Den结构体下组合Person和Speaker
Person
Speaker
}
func (d Den) Speak() string { // 仅为Dennis重写Speak方法
return "I'm quit!"
}
func main() {
den := Den{Person: Person{Name: "Dennis",}}
mike := Speaker{Person: Person{Name: "Michael",}}
fmt.Println(den.Introduce())
fmt.Println(den.Speak())
fmt.Println(mike.Introduce())
fmt.Println(mike.Speak())
}
通过这种方式,我们可以避免为每种类型实现每个所需的方法。
##接口
接口也是同样的道理。但是,如果组合了多个接口,这意味着我们不需要声明已在使用的接口中已经声明的方法。
英文:
Since I'm interested in the feature I have read a few articles about the topic and aggregated it to several reference points.
##Embedding
The feature named "embedding". And it solves the issue with repeated methods implementations. Base syntax:
type Person struct {
Name string
}
type Speaker struct { // Speaker is an abstract concept it has no name
*Person // There is a type without field name. It is Anonymous.
}
##Wrapping and decoration
Yes, there is no OOP but code must be DRY anyway. The clearest way to think about the feature is take this as wrapping structs with methods. So the most veracious way to describe anonymous fields is "decorator" pattern (well known for Pythonistas).
func (a *Speaker) Introduce(){ // But speaker can introduce itself
fmt.Println(a.Name) // We have direct access to a wrapped struct attributes.
}
##Combining and overriding
Also we can combine methods implemented on a structs
func (s Speaker) Speak() string {
return "Blah-blah"
}
type Den struct { // Combine Person and Speaker under Den struct
Person
Speaker
}
func (d Den) Speak() string { // Override Speak method only for Dennis
return "I'm quit!"
}
func main() {
den := Den{Person: Person{Name: "Dennis",}}
mike := Speaker{Person: Person{Name: "Michael",}}
fmt.Println(den.Introduce())
fmt.Println(den.Speak())
fmt.Println(mike.Introduce())
fmt.Println(mike.Speak())
}
In this way we can avoid implementation of each required method for each type.
##Interfaces
Same thing with interfaces. But if several interfaces are combined it means just that we don't need to declare methods which already declared in used interfaces.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论