Is there a better way to achieve the inheritance in go? (In c# we use Abstract class and Interfaces to achieve similar behavior). Please refer below code to understand the problem.
I tried using interface in Go but I am unable to access the data fields of struct.
type Vehicle struct {
Id int
Name string
VehicleType VehicleTypeBase
}
type VehicleTypeBase struct {
Id int
Name string
Milage int
}
type VehicleTypeSedan struct {
VehicleTypeBase
IsABSEnabled bool
}
type VehicleTypeHatchback struct {
VehicleTypeBase
Is4WheelDriveEnabled bool
}
func main() {
var veh Vehicle
veh = Vehicle{
Id: 1,
Name: "Zeep Compass",
VehicleType: VehicleTypeSedan{
Id: 1,
Name: "Sedan",
Milage: 13,
IsABSEnabled: true,
},
}
}
// Above initialization gives error. Here, I would like to understand that how // to achieve inheritance using base class
// in Golang. Is there a way to solve this situation in Go??
The error message is:
.\main.go:40:3: cannot use VehicleTypeSedan literal (type VehicleTypeSedan) as type VehicleTypeBase in field value
It's can work!
type Vehicle struct {
Id int
Name string
VehicleType VehicleTypeInterface
}
type VehicleTypeInterface interface{}
type VehicleTypeBase struct {
Id int
Name string
Milage int
}
type VehicleTypeSedan struct {
VehicleTypeBase
IsABSEnabled bool
}
type VehicleTypeHatchback struct {
VehicleTypeBase
Is4WheelDriveEnabled bool
}
func main() {
var veh Vehicle
veh = Vehicle{
Id: 1,
Name: "Zeep Compass",
VehicleType: VehicleTypeSedan{
VehicleTypeBase: VehicleTypeBase{
Id: 3,
Name: "Sedan",
Milage: 13,
},
IsABSEnabled: true,
},
}
fmt.Printf("%+v", veh)
}
Struct embedding is how go lang prefers. Composition is better than inheritance is the idea.
https://golang.org/doc/effective_go.html#embedding
You should declare an interface for Vehicle and all the Vehicles implement that interface.
This is design level problem than just fixing the error. Since its not clear exactly how you are going to use and deal with Vehicles. I will make some assumptions.
By Embedding way should embed a reusable struct inside specific structs.
type VehicleTypeGeneral struct {
Id int
Name string
Milage int
}
//Embed VehicleTypeGeneral
type VehicleTypeHatchback struct {
VehicleTypeGeneral
Is4WheelDriveEnabled bool
}
If we creates instance vh of VehicleTypeHatchback
then we can access fields of VehicleTypeHatchback
as well as embedded struct VehicleTypeGeneral
like vh.Is4WheelDriveEnabled
and vh.VehicleTypeGeneral.Name
If VehicleTypeGeneral
implement interface like Vehicle
interface then VehicleTypeHatchback
also implements that. You can override by implementing the methods though.
I have added type check example in processSpecificVehicle
function. However these things slow the execution. Instead try to use the approached mentioned in processVehicle
and processAbsVehicle
Also the interfaces should not have to many methods. One or two are enough other wise it is violation of interface segregation principle. Keep the interfaces short and meaningful and design them from the prospective of consumer of interfaces.
Complete example with certain assumptions:
package main
import "fmt"
type Vehicle interface {
GetId() int
GetName() string
}
type AbsVehicle interface {
IsAbsEnabled() bool
}
type VehicleTypeGeneral struct {
Id int
Name string
Milage int
}
func (v *VehicleTypeGeneral) GetId() int{
return v.Id
}
func (v *VehicleTypeGeneral) GetName() string{
return v.Name
}
type VehicleTypeSedan struct {
VehicleTypeGeneral
IsABSEnabled bool
}
func(vs *VehicleTypeSedan) IsAbsEnabled() bool {
return vs.IsABSEnabled
}
type VehicleTypeHatchback struct {
VehicleTypeGeneral
Is4WheelDriveEnabled bool
}
func main() {
println("Hello")
var vehicle = VehicleTypeSedan{IsABSEnabled: true, VehicleTypeGeneral: VehicleTypeGeneral{Id:1001,Name:"Sedan 1", Milage:12}}
processVehicle(&vehicle)
processAbsVehicle(&vehicle)
processSpecificVehicle(&vehicle)
processSedan(&vehicle)
}
func processVehicle(vehicle Vehicle){
println(vehicle.GetId())
println(vehicle.GetName())
}
func processAbsVehicle(vehicle AbsVehicle){
println(vehicle.IsAbsEnabled())
}
func processSpecificVehicle(vehicle Vehicle){
switch v := vehicle.(type) {
case *VehicleTypeSedan:
fmt.Printf("Its a sedan %v with ABS %v ", v.GetName(), v.IsAbsEnabled())
case *VehicleTypeHatchback:
fmt.Printf("Its a VehicleTypeHatchback %v", v.GetName())
default:
fmt.Printf("Its a Vehicle")
}
}
func processSedan(vs *VehicleTypeSedan){
println("
process sedan")
println(vs.VehicleTypeGeneral.Name)
println(vs.IsABSEnabled)
}