我在Golang有一个包含多个模块的项目。由于以下情况,我遇到循环导入问题:
模块游戏包含具有当前游戏状态的结构。另一个模块(Modifier)正在做一些游戏特定的东西和计算,因此修改了游戏状态。因此,Modifier将需要struct Game,但不需要来自Game的任何方法。从Game中调用修饰符,这里我们有循环导入。
游戏启动修改器
修饰符需要游戏结构
在我看来,这是一种常见的情况,所以我想知道如何以最佳方式解决它。我的解决方案是创建第三个模块" Structs"它只包含整个应用程序的所有结构。这是一个很好的解决方案吗?
答案 0 :(得分:3)
使用第3个包选项:
yourgame/
state/
state.go
modifier/
modifier.go
main.go
main.go
会将两个组件粘合在一起:
import "yourgame/state"
import "yourgame/modifier"
type Game struct {
state state.State
modifier modifier.Modifier
}
func main() {
// something like:
var game Game
game.modifier.Modify(game.state)
}
这种方法可能过于紧密耦合了。我会尝试将数据切割成修改器所需的内容,而不是操纵一个本质上是全局的状态对象。
摘要中的推理很难,所以这是我的意思的具体例子。在你的游戏中:
type Object struct {
ID, X, Y int
// more data here
}
type Game struct {
Objects map[int]*Object
}
在你的"修饰符"中,假设我们有一个移动对象的AI模块。如果他关心的只是单个对象的位置,您可以创建一个界面:
// in yourgame/modifier
type Object interface {
GetCoordinates() (int, int)
SetCoordinates(int, int)
}
type Modifier struct {}
func (m *Modifier) Update(obj Object) { }
然后我们只需将这些方法添加到原始对象中:
type (obj *Object) GetCoordinates() (int, int) {
return obj.X, obj.Y
}
type (obj *Object) SetCoordinates(x, y int) {
obj.X, obj.Y = x, y
}
现在您可以将对象传递给修改器而无需循环依赖。
现在,如果事实证明你的"修饰符"界面最终看起来几乎与你的游戏对象完全相同,然后第三个结构包可能是合理的,所以你不会总是重复自己。例如,考虑net/url
。
答案 1 :(得分:0)
通常,如果包B
具有直接读取/修改A.Type
的代码,则该代码应位于包A
中。
如果需要直接访问,至少它的部分应该。
要在单独的包A
和B
之间拆分,您通常会尝试隔离用于访问可以表示为接口的A.Type
的API。然后B
将定义并使用此接口,A.Type
将实现它(隐式地,无需包含B的定义)。
然后,某些内容(可能是A
,可能是一个单独的包)会通过B
或A.Type
值来使用*A.Type
。
或者根据您的设计,可以颠倒关系,B.OtherType
隐式实现A
定义和使用的接口。
或者A
和B
只能通过接口互相使用;这一切都取决于细节。
E.g。也许是这样的:
package Game // "A"
type State struct {
data int // etc
}
func (s State) IsValid() bool { return true }
func (s *State) ChangeY(arg int) error { return nil }
// …etc…
和
package Modifier // "B"
type GameState interface {
IsValid() bool
ChangeY(int) error
}
type M struct {
s GameState
//…
}
func New(s GameState) *M {
return &M{s: s}
}
func (m M) DoSomething() {
if s.IsValid() {
// …
}
s.ChangeY(42)
// …etc…
}
答案 2 :(得分:0)
我在同一个包中定义了类型(本例中为Game)及其所有方法。您甚至无法根据语言spec,
定义从另一个包导入的类型的方法//you should first do
type MyPackageType ImportedType
//and only then
func (foo MyPackageType) Modify() {
...
}