Bill
Bill

Reputation: 25565

Refactor function to make it reusable across types in Go

I have a function that initializes an array of structs from an array of an array of values. This is how I'm doing it currently:

type Loadable interface {
  Load([]interface{})
}

type FooList struct {
  Foos []*Foo
}

func (fl *FooList) Load(vals []interface{}) {
  fl.Foos = make([]*Foo, len(vals))
  for i, v := range vals {
    foo := &Foo{}
    foo.Load(v.([]interface{}))
    fl.Foos[i] = foo
  }
}

This works just fine, but now I also need to initialize BarLists and BazLists which contain Bars and Bazs. Instead of sprinkling the same snippet throughout my code which all look like this:

type BarList struct {
  Bars []*Bar
}

func (fl *BarList) Load(vals []interface{}) {
  fl.Bars = make([]*Bar, len(vals))
  for i, v := range vals {
    bar := &Bar{}
    bar.Load(v.([]interface{}))
    fl.Bars[i] = bar
  }
}

What's the correct way to refactor this code to make it more DRY?

Upvotes: 0

Views: 380

Answers (2)

newacct
newacct

Reputation: 122528

The simplest I can come up with using reflection would be something like this (not tested):

import "reflect"

// example_of_type should be an instance of the type, e.g. Foo{}
// returns slice of pointers, e.g. []*Foo
func Load(vals []interface{}, example_of_type interface()) interface{} {
  type := reflect.TypeOf(example_of_type)
  list := reflect.MakeSlice(type.PtrOf().SliceOf(), len(vals), len(vals))
  for i, v := range vals {
    bar := reflect.New(type)
    bar.Interface().(Loadable).Load(v.([]interface{}))
    list.Index(i).Set(bar)
  }
  return list.Interface()
}

You would use it like:

fl.Foos = Load(vals, Foo{}).([]*Foo)
fl.Bars = Load(vals, Bar{}).([]*Bar)

Upvotes: 0

zzzz
zzzz

Reputation: 91439

The code you show does not violate the DRY principle. The code implementing the Loader interface (I refuse to write the javaism you used) for type FooList and BarList shares only one line - the range statement. Otherwise they're type specific.

As Go has no generics, there's no direct way how to not write type specialized versions in a generic way (modulo poor choices like everything is an interface{} etc. and/or slowing down your code 10 times by using reflection.)

Upvotes: 1

Related Questions