This question is related to Golang function to return an Interface and expands on it.
My setup is the following:
I'm creating the server side of a game where the client can choose different game modes. These modes are realised through different structs with their own implementation of some methods, all of which are their own file in package engines
, for example:
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")
}
In the main package, when a new game is created, I'm using a map to call the newSomething
function of the corresponding game mode. Every engine satifies the interface gameEngine
:
package main
import "engines"
type gameEngine interface {
AddPlayer() (int, error)
}
type GameMode int
const (
SQUARE GameMode = 0
TRIANGLE GameMode = 1
)
This interface is defined within the main package for two primary reasons:
package engines
feels like going back to the situation where classes explicitly state the interfaces they implement, one of the things go is designed not to require.This means that within the files of my engine implementations, like Square.go
and Triangle.go
, I don't have access to the interface and the corresponding new-Functions must return their respective type, which makes them of type func() *Square
and func() *Triangle
, respectively (see above).
Now I can't use these new-functions directly in a map[GameMode]func() gameEngine
because their type is wrong. My current solution is to convert them using a rather verbose inline function (inspired by one of the answers in the linked question):
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
}
This map feels very clunky and artificial, but as far as I can tell, it's the only way to do it with this map approach.
Is there a better way or design pattern to achieve my goal?
func JoinGame(mode GameMode) (int, error)
is part of my client interface, it's the starting point of the whole thing and must keep its signature.type gameEngine interface
should stay in the main package.Hexagon.go
), the only steps necessary should be making the file and registering the newHexagon
function in exactly one place.You would rather define a new type (alias to func) than an interface. type AddPlayer func() (GameMode, error)
And encapsulate this map with functions inside engine
package where you can add new handle-functions (on demand) in separate file with init()
. Like: engine/main.go
:
package engins
type GameMode int
const (
SQUARE = iota
TRIANGLE
)
type AddPlayer func() (GameMode, error)
var newGameFuncs = make(map[GameMode]AddPlayer)
func GetAddPlayerFuncByGameMode(gm GameMode) AddPlayer {
return newGameFuncs[gm]
}
engine/square.go
:
package engins
func init() {
newGameFuncs[SQUARE] = engineSquare{}.NewSquare
}
type engineSquare struct{}
func (engineSquare) NewSquare() (GameMode, error) {
return SQUARE, nil
}
And from main.go
you will be able to use it like:
package main
import e "./engins"
func main() {
e.GetAddPlayerFuncByGameMode(e.SQUARE)
}