I try to implement some kind of open design paradigm with expandability in go, but wonder what would be the gopher way of doing this.
For example I have a vehicle package. Depending on what implementations are available, I want to create an array of all possible vehicles. In this example if just bike is available I want an array [bike] if there is another car implementation I want to have [bike,car]
I don't really get how to do this in go. I thought for an interface in the vehicle package, but I don't really get how to create the array depending on which package is available. And this should be open design, so that if somebody decides to write another vehicle, a truck i.e. it would automatically work upstream.
So this is some example code for the explained issue: If it's not necessary, that they are devided into packages, they could also be in the same package (vehicles)
vehicle.go
package vehicle
type vehicle interface {
drive() string
}
bike.go
package bike
type bike struct {
name string
}
func (b *bike) drive() string {
return "drive bike drive"
}
func NewVehicle() bike {
return &bike{
name: "bike",
}
}
car.go
package car
type carstruct {
name string
}
func (b *car) drive() string {
return "drive cardrive"
}
func NewVehicle() car{
return &car{
name: "car",
}
}
I would think the design of image
and database/sql
and how they co-operate with other libs more idomatic, but if you really need to implent this kind of design, I have come up with some thing hack-y.
The trick is, golang can do function calls during declaration of global vars and that happens before calling any of init
. So we can do the register job during that.
//vehicle.go
import (
"fmt"
)
var _ = register("3",4)
type constructor func() *Vehicle
var reg = make(map[string]consturctor)
func register(name string, cons) bool {
_,ok:=reg[name]
if ok {
return false
}
reg[name] = cons
return true
}
var chosen string
func init() {
//turn that map into a slice if you want
//Chose one "implentation" from the slice/map if you wish
for chosen = range reg {
break // Take the first, it is dirty, I know.
}
if Chosen=="" {
panic("No Implentation!")
}
}
func NewVehicle() *Vehicle {
return reg[chosen]()
}
func main() {
fmt.Println("Hello, playground")
}
//car.go
var carsucc = register("car",constructor(NewCar))
func init() {
if !carsucc {
panic("car: multiple implentation!")
}
//off course you can fail silently
}
func NewCar() Vehicle {
return &Car{}
}