Vererbung durch Komposition
Bevor wir uns in die Vererbung stürzen, wird kurz auf die objektorientierten Konzepte von Go eingegangen. Go kennt Structs welche ähnlich wie Klassen Felder und Methoden beinhalten können. Das nachfolgende Beispiel zeigt ein einfaches Structs mit zwei Feldern und einer Methode.
type Car struct {
wheelCount int // Nur innerhalb des package sichtbar
Brand string // Auch ausserhalb des package sichtbar
}
func (car Car) numberOfWheels() int {
return car.wheelCount
}
Die Sichtbarkeit eines Feldes oder einer Methode wird über die Gross-/Kleinschreibung festgelegt. Felder die klein geschrieben sind, sieht man nur innerhalb des package
. Felder die gross geschrieben sind, sieht man auch ausserhalb des package
. Das gleiche Konzept gilt für die Methoden.
Jedes Struct in Go hat einen Standard-Konstruktor, welcher die Felder auf ihre Nullwerte (z.B. String
auf ""
) initialisiert. Die Felder können auch der Reihe nach befüllt werden. Zudem gibt es auch eine Json-ähnliche Syntax um die Felder zu initialisieren. Das nachfolgende Beispiel zeigt alle drei Varianten.
var c1 Car
var c2 = Car{4, "Tesla"}
var c3 = Car{wheelCount: 3, Brand: "Piaggio"}
Möchte man jetzt als Beispiel einen Porsche erstellen, welcher das Verhalten von Car
übernimmt, muss Vererbung eingesetzt werden. In Go wird keine Vererbung im eigentlichen Sinne verwendet, sondern man bedient sich dem Konzept der Komposition. Dies ist ein bewusster Designentscheid der Sprachentwickler. Dieser Entscheid ist wohl auf Empfehlungen wie Favor Composition over Inheritance zurückzuführen. Das nachfolgende Beispiel veranschaulicht das Prinzip.
type Porsche struct {
Car
}
Das Oberstruct Car
wurde als anonymes Feld von Porsche
definiert. Dadurch erbt Porsche
sämtliches Verhalten von Car
. Es ist dadurch also folgender Aufruf möglich.
var p Porsche
p.numberOfWheels()
Gibt es zwei Methoden mit gleichem Namen wird immer die äussere Methode verwendet. Im folgenden Beispiel bietet Porsche
ebenfalls eine numberOfWheels
-Methode an.
type Car struct {
wheelCount int
}
func (car Car) numberOfWheels() int {
return car.wheelCount
}
type Porsche struct {
Car
}
func (porsche Porsche) numberOfWheels() int {
return 0
}
var p = Porsche{Car{4}}
p.numberOfWheels() // Gibt 0 und nicht 4
p.Car.numberOfWheels() // Gibt 4
In Go ist auch Mehrfachvererbung möglich. Dabei werden einfach zwei anonyme Felder deklariert und das Struct erbt von beiden Feldern. Das Prinzip der Komposition fördert die Wiederverwendung von Code und verhindert Probleme mit der Mehrdeutigkeit (z.B. Diamond Problem) bei Mehrfachvererbung.
In der Literatur findet man oft den Begriff Mixins im Zusammenhang mit der Vererbung von Go. Allerdings wird in der offiziellen Dokumentation von Go dieser Begriff nie erwähnt. Stattdessen wird dieses Konzept Anonyme innere Variablen genannt.
- Youngman, Nathan (2013), Go Object Oriented Design, Gefunden unter: https://nathany.com/good/