I've been playing with interfaces and structures in Golang in an attempt to implement "inheritance", but I am confident that I've done it in a wrong way. It would be easier to explain with an example.
I want to create structures of different living things. I want them to have the GetName() method:
type LivingThingProvider interface {
GetName() string
}
Now, all of them should have names and birthdays. For that, I am creating a structure and embedding the interface to it:
type LivingThing struct {
birthday string
name string
LivingThingProvider
}
I want to add a couple of methods which would be the same for all living beings:
func (this *LivingThing) Initialize() {
this.birthday = time.Now().Format("02.01.2006")
}
func (this LivingThing) GetBirthday() string {
return this.birthday
}
Now, here are the structures that are supposed to "implement" 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))
}
In the main function, I am creating and adding a frog and a robotman to a slice, after that I loop over it:
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())
}
}
Everything works as expected, but if I remove the GetName() method from the Frog or RobotMan structure, it will compile and panic after run:
panic: runtime error: invalid memory address or nil pointer dereference
Here's the playground link: https://play.golang.org/p/h2VgvdcXJQA
My questions are the following:
1. Is what I've done "dirty" in terms of Go? If so, how to do it in a correct way?
1a. Especially, is it fine to assign the structure itself to its embedded interface field (fr.LivingThingProvider = fr)?
2. Why doesn't Go compiler check if Frog and RobotMan implement the LivingThingProvider interface?
Thank you very much in advance!
@mkopriva answer it and should get the credit but it looks like some explanation needed for classical inheritance programmers.
Interface define behavior like Name and Birthday, (In Go its idiomatic to omit the Get). Structs implement these interface behavior. Not embedding them.
Implementing an interface is all you need to do in order to "be" this interface. AKA duck type.
When struct T implement String() string
T become a Stringer.
In this particular example we can see why In Golang a one method interface and interface embedding is so common.
Since RobotMan do not have a birthday really it whould be better in my opinion to create three interfaces Namer: Name() string
, Burner:Birthday() time.Time
and
type LivingThingProvider interface {
Namer
Burner
}
Struct composition is not a replacement for inheritance its a different way to reuse code.