【发布时间】:2019-04-27 12:51:04
【问题描述】:
此问题与Golang function to return an Interface 相关并对其进行了扩展。
我的设置如下:
我正在创建游戏的服务器端,客户端可以在其中选择不同的游戏模式。这些模式是通过不同的结构体实现的,其中一些方法自己实现,都是package engines中自己的文件,例如:
package engines
import "fmt"
type Square struct {
tiles []int
players []bool
}
func NewSquare() *Square {
return &Square{
[25]int{}[:],
[]bool{false, false},
}
}
func (e *Square) AddPlayer() (int, error) {
for id := range e.players {
if !e.players[id] {
e.players[id] = true
return id, nil
}
}
return -1, fmt.Printf("game already full")
}
在主包中,当一个新游戏被创建时,我正在使用一个地图来调用对应游戏模式的newSomething函数。每个引擎都满足接口gameEngine:
package main
import "engines"
type gameEngine interface {
AddPlayer() (int, error)
}
type GameMode int
const (
SQUARE GameMode = 0
TRIANGLE GameMode = 1
)
这个接口在主包中定义有两个主要原因:
- 将接口定义放在使用该接口的同一文件中可以提高可读性。如果我不确定某个对象的功能,我可以滚动到接口定义并检查。
- 将接口定义放在
package engines中感觉就像回到了类显式声明它们实现的接口的情况,其中一件事是设计为不需要。
这意味着在我的引擎实现的文件中,例如Square.go 和Triangle.go,我无权访问接口并且相应的新函数必须返回它们各自的类型,这使得它们的类型为@分别为 987654331@ 和 func() *Triangle(见上文)。
现在我不能直接在map[GameMode]func() gameEngine 中使用这些新函数,因为它们的类型是错误的。我目前的解决方案是使用一个相当冗长的内联函数来转换它们(灵感来自链接问题中的一个答案):
var engine gameEngine
var newGameFuncs = map[GameMode]func() gameEngine {
SQUARE: func() gameEngine { return engines.NewSquare() },
TRIANGLE: func() gameEngine { return engines.NewTriangle() },
}
func JoinGame(mode GameMode) (int, error) {
engine = newGameFuncs[mode]()
id, err := engine.AddPlayer()
// Some other stuff, too
return id, nil
}
这张地图感觉非常笨拙和人工,但据我所知,这是使用这种地图方法的唯一方法。
有没有更好的方法或设计模式来实现我的目标?
-
func JoinGame(mode GameMode) (int, error)是我的客户端界面的一部分,它是整个事情的起点,必须保留它的签名。 -
type gameEngine interface应该留在主包中。 - 在实现新模式(例如,
Hexagon.go)时,唯一需要的步骤应该是在一个地方创建文件并注册newHexagon函数。
【问题讨论】:
-
如果闭包困扰您,您可以用 switch 语句替换 map,但您现在拥有的是一个合理的解决方案。
-
对,我什至没有想到 switch 语句(我最初的解决方案,没有工作,只是映射
SQUARE: engines.NewSquare(),但现在添加了闭包,将映射变成了一个函数内部只有一个开关确实看起来很简单。如果我在性能很重要的情况下面临这个决定,你知道哪个性能更好吗? -
差别很小。一如既往,measure it,但仅限于必要时。
标签: go