使用混合结构类型的Go地图和切片

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

Go maps and slices with mixed struct types

问题

我正在尝试学习如何使用Go语言创建一个简单的事件存储器,并具备创建投影的能力。我在处理包含不同类型结构体的切片和映射时遇到了困难。这样做的目的是希望开发人员能够根据需要创建实现IEntity和IEvent接口的多个结构体,这些结构体具有不同的字段。

我之前主要使用JavaScript/Node.js,对C/C++/Java也有一些基础知识。我可能在这里过于追求类/继承模式,需要一些帮助来了解如何在Go中实现相同的功能。

以下是要翻译的代码:

  1. package main
  2. import (
  3. "sync"
  4. "time"
  5. uuid "github.com/satori/go.uuid"
  6. )
  7. // IEntity描述一个实体,即按照时间顺序解析的所有事件的总和
  8. type IEntity interface {
  9. }
  10. // IProjection描述投影结构的接口
  11. type IProjection interface {
  12. Lock()
  13. Unlock()
  14. Get(id uuid.UUID) IEntity
  15. Set(id uuid.UUID, entity IEntity)
  16. GetLastEventTime() time.Time
  17. Append(event IEvent)
  18. }
  19. // IEvent描述事件的接口,系统中添加的任何事件都需要实现此接口
  20. type IEvent interface {
  21. Resolve(projection IProjection)
  22. }
  23. // EventVault是保存所有事件并允许创建投影的基本结构体
  24. type EventVault struct {
  25. sync.Mutex
  26. events []IEvent
  27. }
  28. // Append将IEvent添加到投影中,并运行该事件的IEvent.Resolve方法
  29. func (vault *EventVault) Append(event IEvent) {
  30. vault.Lock()
  31. defer vault.Unlock()
  32. vault.events = append(vault.events, event)
  33. }
  34. // Project使用所有事件创建一个投影,直到选择的结束时间为止
  35. func (vault *EventVault) Project(endTime time.Time, projection IProjection) IProjection {
  36. lastEventTime := projection.GetLastEventTime()
  37. for index := range vault.events {
  38. event := vault.events[index]
  39. if event.Time.After(lastEventTime) && event.Time.Before(endTime) {
  40. projection.Append(event)
  41. }
  42. }
  43. return projection
  44. }
  45. // Projection计算并存储附加到其中的事件的投影
  46. type Projection struct {
  47. sync.Mutex
  48. events []IEvent
  49. entities map[uuid.UUID]IEntity
  50. }
  51. // Get根据ID从投影中返回一个IEntity结构体(用于IEvent.Resolve方法)
  52. func (projection *Projection) Get(id uuid.UUID) IEntity {
  53. return projection.entities[id]
  54. }
  55. // Set将IEntity添加到投影中(用于IEvent.Resolve方法)
  56. func (projection *Projection) Set(id uuid.UUID, entity IEntity) {
  57. projection.entities[id] = entity
  58. }
  59. // GetLastEventTime返回最后添加的事件的时间
  60. func (projection *Projection) GetLastEventTime() time.Time {
  61. event := projection.events[len(projection.events)-1]
  62. if event == nil {
  63. return time.Unix(0, 0)
  64. }
  65. return event.Time
  66. }
  67. // Append将IEvent添加到投影中,并运行该事件的IEvent.Resolve方法
  68. func (projection *Projection) Append(event IEvent) {
  69. projection.Lock()
  70. projection.events = append(projection.events, event)
  71. event.Resolve(projection)
  72. projection.Unlock()
  73. }
  74. // ------------------ 以下是上述系统的使用示例 ------------------
  75. // PlayerEntity是一个可用于测试的示例实体
  76. type PlayerEntity struct {
  77. ID uuid.UUID
  78. Name string
  79. Birthday time.Time
  80. UpdatedAt time.Time
  81. }
  82. // AddPlayerEvent是一个可用于测试的示例事件
  83. type AddPlayerEvent struct {
  84. ID uuid.UUID
  85. Time time.Time
  86. Name string
  87. }
  88. // Resolve将创建一个新的PlayerEntity并将其添加到投影中
  89. func (event *AddPlayerEvent) Resolve(projection IProjection) {
  90. player := PlayerEntity{ID: uuid.NewV4(), Name: event.Name, UpdatedAt: event.Time}
  91. projection.Set(player.ID, &player)
  92. }
  93. // SetBirthdayPlayerEvent是一个可用于测试的示例事件
  94. type SetBirthdayPlayerEvent struct {
  95. ID uuid.UUID
  96. Time time.Time
  97. Birthday time.Time
  98. }
  99. // Resolve将更改PlayerEntity的名称
  100. func (event *SetBirthdayPlayerEvent) Resolve(projection IProjection) {
  101. player := *projection.Get(event.ID)
  102. player.Birthday = event.Birthday
  103. player.UpdatedAt = event.Time
  104. }
  105. func main() {
  106. vault := EventVault{}
  107. event1 := AddPlayerEvent{ID: uuid.NewV4(), Time: time.Now(), Name: "Lisa"}
  108. vault.Append(&event1)
  109. birthday, _ := time.Parse("2006-01-02", "2017-03-04")
  110. event2 := SetBirthdayPlayerEvent{ID: event1.ID, Time: time.Now(), Birthday: birthday}
  111. vault.Append(&event2)
  112. }

我遇到的错误是:

  1. ./main.go:47: event.Time undefined (type IEvent has no field or method Time)
  2. ./main.go:79: event.Time undefined (type IEvent has no field or method Time)
  3. ./main.go:122: invalid indirect of projection.Get(event.ID) (type IEntity)

结构体类型可能丢失,因此我需要另一种方法来存储它们以便将它们放入映射和切片中,但是如何实现呢?

英文:

I'm trying to learn how to work with Go by creating a simple event store with the ability to create projections. I get stuck on how to work with slices and maps that contains structs of mixed types. The point with this would be that I want a developer to create as many structs implementing IEntity and IEvent as needed with various fields.

I come from a JavaScript/Node.js background with some basic knowledge of C/C++/Java and I'm probably looking to much for class/inheritance patterns here and need some help on how to get the same functionality in Go.

  1. package main
  2. import (
  3. "sync"
  4. "time"
  5. uuid "github.com/satori/go.uuid"
  6. )
  7. // IEntity describes an entity, a struct that is the sum of all events resolved in a chronological order
  8. type IEntity interface {
  9. }
  10. // IProjection describes the interface for a projection struct
  11. type IProjection interface {
  12. Lock()
  13. Unlock()
  14. Get(id uuid.UUID) IEntity
  15. Set(id uuid.UUID, entity IEntity)
  16. GetLastEventTime() time.Time
  17. Append(event IEvent)
  18. }
  19. // IEvent describes the interface for a event, any event added to the system needs to implement this interface
  20. type IEvent interface {
  21. Resolve(projection IProjection)
  22. }
  23. // EventVault is the base struct that keeps all the events and allows for projections to be created
  24. type EventVault struct {
  25. sync.Mutex
  26. events []IEvent
  27. }
  28. // Append adds a IEvent to the projection and runs that events IEvent.Resolve method
  29. func (vault *EventVault) Append(event IEvent) {
  30. vault.Lock()
  31. defer vault.Unlock()
  32. vault.events = append(vault.events, event)
  33. }
  34. // Project creates a projection with entities from all events up until the choosen end time
  35. func (vault *EventVault) Project(endTime time.Time, projection IProjection) IProjection {
  36. lastEventTime := projection.GetLastEventTime()
  37. for index := range vault.events {
  38. event := vault.events[index]
  39. if event.Time.After(lastEventTime) && event.Time.Before(endTime) {
  40. projection.Append(event)
  41. }
  42. }
  43. return projection
  44. }
  45. // Projection caculates and stores a projection of the events appended to it with the Append method
  46. type Projection struct {
  47. sync.Mutex
  48. events []IEvent
  49. entities map[uuid.UUID]IEntity
  50. }
  51. // Get returns an IEntity struct from an id (for use in the IEvent.Resolve method)
  52. func (projection *Projection) Get(id uuid.UUID) IEntity {
  53. return projection.entities[id]
  54. }
  55. // Set add a IEntity to the projection (for use in the IEvent.Resolve method)
  56. func (projection *Projection) Set(id uuid.UUID, entity IEntity) {
  57. projection.entities[id] = entity
  58. }
  59. // GetLastEventTime returns the time for the event that was added last
  60. func (projection *Projection) GetLastEventTime() time.Time {
  61. event := projection.events[len(projection.events)-1]
  62. if event == nil {
  63. return time.Unix(0, 0)
  64. }
  65. return event.Time
  66. }
  67. // Append adds a IEvent to the projection and runs that events IEvent.Resolve method
  68. func (projection *Projection) Append(event IEvent) {
  69. projection.Lock()
  70. projection.events = append(projection.events, event)
  71. event.Resolve(projection)
  72. projection.Unlock()
  73. }
  74. // ------------------ Below is usage of above system ------------------
  75. // PlayerEntity is a sample entity that can be used for testing
  76. type PlayerEntity struct {
  77. ID uuid.UUID
  78. Name string
  79. Birthday time.Time
  80. UpdatedAt time.Time
  81. }
  82. // AddPlayerEvent is a sample event that can be used for testing
  83. type AddPlayerEvent struct {
  84. ID uuid.UUID
  85. Time time.Time
  86. Name string
  87. }
  88. // Resolve will create a new PlayerEntity and add that to the projection
  89. func (event *AddPlayerEvent) Resolve(projection IProjection) {
  90. player := PlayerEntity{ID: uuid.NewV4(), Name: event.Name, UpdatedAt: event.Time}
  91. projection.Set(player.ID, &player)
  92. }
  93. // SetBirthdayPlayerEvent is a sample event that can be used for testing
  94. type SetBirthdayPlayerEvent struct {
  95. ID uuid.UUID
  96. Time time.Time
  97. Birthday time.Time
  98. }
  99. // Resolve will change the name on a PlayerEntity
  100. func (event *SetBirthdayPlayerEvent) Resolve(projection IProjection) {
  101. player := *projection.Get(event.ID)
  102. player.Birthday = event.Birthday
  103. player.UpdatedAt = event.Time
  104. }
  105. func main() {
  106. vault := EventVault{}
  107. event1 := AddPlayerEvent{ID: uuid.NewV4(), Time: time.Now(), Name: "Lisa"}
  108. vault.Append(&event1)
  109. birthday, _ := time.Parse("2006-01-02", "2017-03-04")
  110. event2 := SetBirthdayPlayerEvent{ID: event1.ID, Time: time.Now(), Birthday: birthday}
  111. vault.Append(&event2)
  112. }

The errors I get are

  1. ./main.go:47: event.Time undefined (type IEvent has no field or method Time)
  2. ./main.go:79: event.Time undefined (type IEvent has no field or method Time)
  3. ./main.go:122: invalid indirect of projection.Get(event.ID) (type IEntity)

The struct types are probably lost so I need another way of storing them to put them into the maps and slices but how?

答案1

得分: 1

在Go语言中,当你声明一个接口时,比如:

  1. type IEntity interface {
  2. }

你定义了可以在该接口中进行的所有操作,而无需进行类型转换。所以在这里,你没有为该接口定义任何功能。如果你想要功能,你需要给它添加一个方法,比如:

  1. type IEntity interface {
  2. Time() time.Time
  3. }

任何想要与该接口一起使用的类型都必须实现这些函数,例如:

  1. func (a AddPlayerEvent) Time() time.Time {
  2. return a.Time
  3. }

参见文档

然后你可以使用这些方法之一:

  1. func (projection *Projection) Append(event IEvent) {
  2. ...
  3. event.Time()
  4. ...
  5. }

还有两个注意事项:

  • 当遍历一个map时,你可以使用k, v := range my_map
  • *projection.Get(event.ID) 试图解引用一个非指针类型。
英文:

In golang when you declare an interface like

  1. type IEntity interface {
  2. }

You define everything that can be done with this interface without a type cast. So here you have defined no functionality for this interface. If you want functionality than you have to give it a method like

  1. type IEntity interface {
  2. Time() time.Time
  3. }

Any type that wants to be used with that interface must implement those functions i.e.

  1. func (a AddPlayerEvent) Time() time.Time {
  2. return a.Time
  3. }

See the docs

Then you can use any of those methods

  1. func (projection *Projection) Append(event IEvent) {
  2. ...
  3. event.Time()
  4. ...
  5. }

2 more notes

  • When iterating through a map you can use k, v := range my_map
  • *projection.Get(event.ID) attempts to deference a non pointer type

huangapple
  • 本文由 发表于 2017年3月21日 18:31:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/42924518.html
匿名

发表评论

匿名网友

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

确定