How to keep code DRY in Golang

huangapple go评论74阅读模式
英文:

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类型实现所有的方法来满足接口要求。但是KingdomPhylum方法对于两种类型来说是相同的。有没有办法只为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())
}

通过这种方式,我们可以避免为每种类型实现每个所需的方法。

##接口

接口也是同样的道理。但是,如果组合了多个接口,这意味着我们不需要声明已在使用的接口中已经声明的方法。

Playground

Dennis Suratna的博客文章

文档

英文:

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.

Playground

Dennis Suratna's Blog article

Docs

huangapple
  • 本文由 发表于 2016年10月25日 22:03:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/40242142.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定