3

General question

how can I design an interface that can support both

// v1beta1.Deployment
type Deployment struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec v1beta1.DeploymentSpec
    Status v1beta1.DeploymentStat
}
type DeploymentInterface interface {
    Create(*v1beta1.Deployment) (*v1beta1.Deployment, error)
    Update(*v1beta1.Deployment) (*v1beta1.Deployment, error)
    UpdateStatus(*v1beta1.Deployment) (*v1beta1.Deployment, error)
}
// v1.Deployment
type Deployment struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec v1.DeploymentSpec
    Status v1.DeploymentStat
}
type DeploymentInterface interface {
    Create(*v1.Deployment) (*v1.Deployment, error)
    Update(*v1.Deployment) (*v1.Deployment, error)
    UpdateStatus(*v1.Deployment) (*v1.Deployment, error)
}

which have different parameter and return types?


Details

The above two interfaces are from kubernetes go-client, they define different versions of the API. Since we have to support both of them for different versions of clusters we are running and I don't want to copy our application code for every version, I want to design an interface that can support different versions of Deployment.

The current code of our application has a lot of helper functions relying on the specific type, for an example:

func (s *KubeControllerService) deploymentCustomImage(deployment *v1beta1.Deployment, appGitBuildConfig *models.AppGitBuildConfig) *v1beta1.Deployment {
}

And we have hundreds of them. It would be very hard to support a new version by copying each function and impossible to maintain such code.

For what I know, since go's lack of generics, to support two different types the only viable way is to use interface. But i'm facing methods with different types of parameters and return values, I have no idea how to design for this scenario in go.

1 Answer 1

3

One way of doing can be like demonstrated as below. Here since I know my types I get the values out using the reflect package & calculate the area. You can run in on playground. Check out reflect package here


import (
    "fmt"
    "reflect"
)

type shape interface {
    area(interface{}, interface{}) interface{}
}

type rect struct{}
type tri struct{}

func (r rect)area(a,b interface{}) interface{} {
    fmt.Println(reflect.ValueOf(r).Type(), reflect.ValueOf(b).Kind())

    lenf := reflect.ValueOf(a).Float()
    widthf := reflect.ValueOf(b).Float()

    return lenf * widthf
}

func (t tri)area(a,b interface{}) interface{} {
    l := reflect.ValueOf(a).Int()
    h := reflect.ValueOf(b).Int()

    return h * l
}

func main() {
    r := rect{}
    t := tri{}
    fmt.Println("Area of rectangle:", r.area(4.5, 4.0))
    fmt.Println("Area of triangle:", t.area(4, 4))
}

So for your case the DeploymentInterface interface would look like

type DeploymentInterface interface {
    Create(interface{}) (interface{}, error)
    Update(interface{}) (interface{}, error)
    UpdateStatus(interface{}) (interface{}, error)
}
2
  • 1
    I considered every patterns but forgot I can just use an empty interface with reflect 🤣 Thanks, this is really a great method for solving this problem, but, for learning purposes, I hope you don't mind me to leave this question open for a little while to see if there're better paradigms without reflection coming.
    – dotslashlu
    Commented Mar 7, 2020 at 7:59
  • No problem. If there's a better solution, I want to learn as well.
    – asr9
    Commented Mar 7, 2020 at 12:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.