在带有嵌入式接口字段的Go中正确实现继承

时间:2018-08-25 21:39:47

标签: inheritance go interface embedding

我一直在尝试使用Golang中的接口和结构来实现“继承”,但是我相信自己做错了方法。用一个例子来解释会更容易。

我想创造不同生物的结构。我希望他们拥有GetName()方法:

type LivingThingProvider interface {
    GetName() string
}

现在,所有的人都应该有名字和生日。为此,我正在创建一个结构并将接口嵌入其中:

type LivingThing struct {
    birthday string
    name     string
    LivingThingProvider
}

我想添加几种对所有生物都相同的方法:

func (this *LivingThing) Initialize() {
    this.birthday = time.Now().Format("02.01.2006")
}

func (this LivingThing) GetBirthday() string {
    return this.birthday
}

现在,这里是应该“实现” LivingThing的结构:

type Frog struct {
    insectsEaten int
    LivingThing
}

func (this Frog) GetName() string {
    return fmt.Sprintf("%s-the-Frog-proud-eater-of-%d-insects", this.name, this.insectsEaten)
}

type RobotMan struct {
    LivingThing
}

func (this RobotMan) GetName() string {
    h := sha256.New()
    h.Write([]byte(this.birthday))
    return fmt.Sprintf("%s-%X", this.name, h.Sum(nil))
}

在主要功能中,我创建了一个青蛙和一个机器人手,并将其添加到一个切片中,然后在其上循环:

func main() {
    fr := Frog{}
    fr.name = "Dizzy"
    fr.insectsEaten = 586
    fr.LivingThingProvider = fr

    rm := RobotMan{}
    rm.name = "Bender"
    rm.LivingThingProvider = rm

    fr.Initialize()
    rm.Initialize()

    entities := []LivingThing{fr.LivingThing, rm.LivingThing}

    for _, ent := range entities {
        fmt.Printf("Hi, I am %s!\n", ent.GetName())
        fmt.Printf("I was born on the %s.\n", ent.GetBirthday())
    }
}

一切正常,但是如果我从Frog或RobotMan结构中删除GetName()方法,则它将在运行后编译并出现紧急情况:

panic: runtime error: invalid memory address or nil pointer dereference

这是游乐场链接:https://play.golang.org/p/h2VgvdcXJQA

我的问题如下:

1。就Go而言,我做的是“肮脏的”吗?如果是这样,如何以正确的方式做到这一点?

1a。,尤其是,可以将结构本身分配给其嵌入式接口字段(fr.LivingThingProvider = fr)吗?

2。为什么Go编译器不检查Frog和RobotMan是否实现LivingThingProvider接口?

非常感谢您!

1 个答案:

答案 0 :(得分:0)

@mkopriva回答了这一问题,应该得到功劳,但看起来像是经典继承程序员需要的一些解释。

接口定义了诸如名称和生日之类的行为(在Go语言中,习惯用法省略了Get)。结构实现这些接口行为。不嵌入它们。

实现一个接口就是您要做的就是“成为”这个接口。又叫鸭型。

当结构T的实现(Bounded a, Enum a) => a T成为Stringer时。

在这个特定的示例中,我们可以看到为什么在Golang中一个方法的接口和接口嵌入如此常见。

由于RobotMan确实没有生日,所以我认为最好创建三个接口Namer:Int,Burner:String() string

Name() string

结构组合不是继承的替代品,而是重用代码的另一种方式。