Golang 多态参数和返回值

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

Golang polymorphic parameters and returns

问题

func ToModelList(cats *[]*Cat) *[]*CatModel {
list := *cats
newModelList := []*CatModel{}
for i := range list {
obj := list[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}

func ToModelList(dogs *[]*Dog) *[]*DogModel {
list := *dogs
newModelList := []*DogModel{}
for i := range list {
obj := list[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}

有没有办法将这两个函数合并,这样我就可以这样做:

func ToModelList(objs *[]*interface{}) *[]*interface{} {
list := *objs
// 确定objs/list的结构体类型
newModelList := []*interface{}
// 将newModelList类型转换为正确的数组结构体类型
for i := range list {
obj := list[i]
// 根据objs的类型对obj进行类型转换
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}

英文:

Say I have functions:

  1. func ToModelList(cats *[]*Cat) *[]*CatModel {
  2. list := *cats
  3. newModelList := []*CatModel{}
  4. for i := range list {
  5. obj := obj[i]
  6. newModelList = append(newModelList, obj.ToModel())
  7. }
  8. return &newModelList
  9. }
  10. func ToModelList(dogs *[]*Dog) *[]*DogModel {
  11. list := *dogs
  12. newModelList := []*DogModel{}
  13. for i := range list {
  14. obj := obj[i]
  15. newModelList = append(newModelList, obj.ToModel())
  16. }
  17. return &newModelList
  18. }

Is there a way to combine those two so I can do something like

  1. func ToModelList(objs *[]*interface{}) *[]*interface{} {
  2. list := *objs
  3. // figure out what type struct type objs/list are
  4. newModelList := []*interface{}
  5. // type cast newModelList to the correct array struct type
  6. for i := range list {
  7. obj := obj[i]
  8. // type cast obj based on objs's type
  9. newModelList = append(newModelList, obj.ToModel())
  10. }
  11. return &newModelList
  12. }

答案1

得分: 3

首先,切片已经是一个引用,除非你需要改变切片本身,否则不需要将其作为指针传递。

其次,interface{} 可以是一个对象或对象的指针,不需要使用 *interface{}。

我不确定你想要实现什么,但你可以像这样做:

  1. package main
  2. // Cat, Dog 的接口
  3. type Object interface {
  4. ToModel() Model
  5. }
  6. // CatModel, DogModel 的接口
  7. type Model interface {
  8. Name() string
  9. }
  10. type Cat struct {
  11. name string
  12. }
  13. func (c *Cat) ToModel() Model {
  14. return &CatModel{
  15. cat: c,
  16. }
  17. }
  18. type CatModel struct {
  19. cat *Cat
  20. }
  21. func (c *CatModel) Name() string {
  22. return c.cat.name
  23. }
  24. type Dog struct {
  25. name string
  26. }
  27. func (d *Dog) ToModel() Model {
  28. return &DogModel{
  29. dog: d,
  30. }
  31. }
  32. type DogModel struct {
  33. dog *Dog
  34. }
  35. func (d *DogModel) Name() string {
  36. return d.dog.name
  37. }
  38. func ToModelList(objs []Object) []Model {
  39. newModelList := []Model{}
  40. for _, obj := range objs {
  41. newModelList = append(newModelList, obj.ToModel())
  42. }
  43. return newModelList
  44. }
  45. func main() {
  46. cats := []Object{
  47. &Cat{name: "felix"},
  48. &Cat{name: "leo"},
  49. &Dog{name: "octave"},
  50. }
  51. modelList := ToModelList(cats)
  52. for _, model := range modelList {
  53. println(model.Name())
  54. }
  55. }

你为 Cat、Dog 等定义了接口,以及为 Model 定义了接口。然后按照你的需求进行实现,使用 ToModelList() 函数非常简单。

英文:

First, slices are already a reference, unless you need to change the slice itself, you do not need to pass it as a pointer.
Second, an interface{} can be regardless an object or a pointer to an object. You do not need to have *interface{}.

I am not sure what you are trying to achieve but you could do something like this:

  1. package main
  2. // Interface for Cat, Dog
  3. type Object interface {
  4. ToModel() Model
  5. }
  6. // Interface for CatModel, DogModel
  7. type Model interface {
  8. Name() string
  9. }
  10. type Cat struct {
  11. name string
  12. }
  13. func (c *Cat) ToModel() Model {
  14. return &CatModel{
  15. cat: c,
  16. }
  17. }
  18. type CatModel struct {
  19. cat *Cat
  20. }
  21. func (c *CatModel) Name() string {
  22. return c.cat.name
  23. }
  24. type Dog struct {
  25. name string
  26. }
  27. func (d *Dog) ToModel() Model {
  28. return &DogModel{
  29. dog: d,
  30. }
  31. }
  32. type DogModel struct {
  33. dog *Dog
  34. }
  35. func (d *DogModel) Name() string {
  36. return d.dog.name
  37. }
  38. func ToModelList(objs []Object) []Model {
  39. newModelList := []Model{}
  40. for _, obj := range objs {
  41. newModelList = append(newModelList, obj.ToModel())
  42. }
  43. return newModelList
  44. }
  45. func main() {
  46. cats := []Object{
  47. &Cat{name: "felix"},
  48. &Cat{name: "leo"},
  49. &Dog{name: "octave"},
  50. }
  51. modelList := ToModelList(cats)
  52. for _, model := range modelList {
  53. println(model.Name())
  54. }
  55. }

You define interfaces for your Cat, Dogs etc and for your Model. Then you implement them as you want and it is pretty straight forward to do ToModelList().

答案2

得分: 1

你可以让 *CatModel 和 *DogModel 都实现 PetModel 接口,并在函数签名中返回 []Pet。

  1. func (cats []*Cat) []PetModel {
  2. ...
  3. return []PetModel{...}
  4. }
  5. func (dogs []*Dog) []PetModel {
  6. ...
  7. return []PetModel{...}
  8. }

顺便说一下,在 Go 语言中返回指向切片的指针是没有意义的。

英文:

you can make *CatModel and *DogModel both implement type PetModel {} interface, and just return []Pet in function signature.

  1. func (cats []*Cat) []PetModel {
  2. ...
  3. return []*CatModel {...}
  4. }
  5. func (dogs []*Dog) []PetModel {
  6. ...
  7. return []*DogModel {...}
  8. }

BTW: return a pointer of a slice in golang is useless.

答案3

得分: 0

如果去除多余的赋值和不必要的切片指针,你会发现剩下的代码很少,而为每个模型类型复制代码看起来并不那么糟糕。

  1. func CatsToCatModels(cats []*Cat) []*CatModel {
  2. var result []*CatModel
  3. for _, cat := range cats {
  4. result = append(result, cat.ToModel())
  5. }
  6. return result
  7. }

除非这段代码在很多地方都被使用,否则我建议将其内联,因为这是一段简单的代码,内联后只有4行。

是的,你可以用interface{}替换所有类型,使代码变得通用,但我认为在这里这样做并不是一个好的权衡。

英文:

If you strip away redundant assignments, and unnecessary pointers-to-slices, you'll find you have little code left, and duplicating it for each of your model types doesn't look so bad.

  1. func CatsToCatModels(cats []*Cat) []*CatModel {
  2. var result []*CatModel
  3. for _, cat := range cats {
  4. result = append(result, cat.ToModel())
  5. }
  6. return result
  7. }

Unless this code is used in a lot of places I'd also consider just inlining it, since it's trivial code and only 4 lines when inlined.

Yes, you can replace all the types with interface{} and make the code generic, but I don't think it's a good tradeoff here.

huangapple
  • 本文由 发表于 2014年2月8日 04:06:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/21636805.html
匿名

发表评论

匿名网友

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

确定