Search code examples
gostructinterfaceabstraction

Open Design and expandability in GO


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",
    }
}

Solution

  • 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{}
    }