重构函数以使其在Go中可跨类型重用

huangapple go评论159阅读模式
英文:

Refactor function to make it reusable across types in Go

问题

我有一个函数,它从一个值的数组的数组中初始化一个结构体数组。这是我目前的做法:

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

这个方法运行良好,但现在我还需要初始化包含 Bars 和 Bazs 的 BarLists 和 BazLists。而不是在我的代码中到处散布相同的代码片段,它们看起来都像这样:

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

重构这段代码以使其更加DRY的正确方法是什么?

英文:

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?

答案1

得分: 1

你展示的代码并没有违反DRY原则。实现Loader接口的代码(我拒绝使用你所说的"javaism")对于FooListBarList类型只共享了一行代码 - range语句。除此之外,它们是类型特定的。

由于Go语言没有泛型,没有直接的方法来以一种"通用"的方式编写"类型专用"的版本(除非使用一些不好的选择,比如将所有东西都定义为interface{},或者通过使用反射将代码的执行速度降低10倍)。

英文:

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.)

答案2

得分: 0

我可以为您提供以下代码的翻译:

import "reflect"

// example_of_type 应该是类型的实例,例如 Foo{}
// 返回指针的切片,例如 []*Foo
func Load(vals []interface{}, example_of_type interface{}) interface{} {
  typ := reflect.TypeOf(example_of_type)
  list := reflect.MakeSlice(reflect.PtrTo(typ).SliceOf(), len(vals), len(vals))
  for i, v := range vals {
    bar := reflect.New(typ)
    bar.Interface().(Loadable).Load(v.([]interface{}))
    list.Index(i).Set(bar)
  }
  return list.Interface()
}

您可以像这样使用它:

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

请注意,这只是代码的翻译,我无法测试它的正确性。

英文:

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)

huangapple
  • 本文由 发表于 2013年9月2日 01:36:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/18561074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定