【问题标题】:functions creating instances of structs implementing an interface创建实现接口的结构实例的函数
【发布时间】: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
)

这个接口在主包中定义有两个主要原因:

  1. 将接口定义放在使用该接口的同一文件中可以提高可读性。如果我不确定某个对象的功能,我可以滚动到接口定义并检查。
  2. 将接口定义放在package engines 中感觉就像回到了类显​​式声明它们实现的接口的情况,其中一件事是设计为不需要。

这意味着在我的引擎实现的文件中,例如Square.goTriangle.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


【解决方案1】:

你宁愿定义一个新类型(func 的别名)而不是一个接口。 type AddPlayer func() (GameMode, error)

并使用engine 包中的函数封装此映射,您可以在其中使用init() 在单独的文件中添加新的句柄函数(按需)。像: 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
}

main.go 你可以像这样使用它:

package main

import e "./engins"

func main() {
    e.GetAddPlayerFuncByGameMode(e.SQUARE)
}

【讨论】:

    猜你喜欢
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-25
    • 2020-05-15
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多