当有接口及其在不同包中的实现时如何管理循环依赖

我的项目结构如下所示:


代码结构:


hypervisor

├── hypervisor.go

├── hyperv

│   └── hyperv.go

└── virtualbox

    ├── vbox.go

    └── vboxprops.go

源代码:


//hypervisor/hypervisor.go

package hypervisor


type Hypervisor interface {

    Start(vmName string) error


    ListMounts(vmName string) ([]MountPath, error)


    //....

}



type MountPath struct {

    HostPath  string

    GuestPath string

}



func detect() (Hypervisor, error) {

    return &virtualbox.Virtualbox{}, nil  // <<1 HERE

}


// ... other code

并有另一个(嵌套)包:


//hypervisor/virtualbox/vbox.go

package virtualbox


type Virtualbox struct {

}


func (*Virtualbox) Start(vmName string) error {

    return vboxManage("startvm", vmName, "--type", "headless").Run()

}


func (*Virtualbox) ListMounts(vmName string) ([]hypervisor.MountPath, error) { // <<2 HERE

    // ....


// ... other code

正如所见,当然,这样的代码会导致import cycle not allowed . 因为:

  1. hypervisorpcakge 引用virtualbox.VirtualBox类型

  2. virtualbox包引用hypervisor.MountPath类型

我知道如果我将结构移动MounthPath到另一个包会解决问题,但我认为这不是正确的解决方案设计方式。

有什么建议吗?


幕布斯6054654
浏览 152回答 2
2回答

素胚勾勒不出你

例如,我会做的一种最简单的方法是将实体分成entities包(在这种情况下:Hypervisor和Virtualbox结构是实体或任何你想调用的东西)。这是我认为最常见的设计,因此内部包使用的每个结构都不会导致循环依赖。使用示例:所有time包结构都在顶级包级别。time.Time{},time.Duration{}等time.Duration不在time/duration包装上。

眼眸繁星

在大多数情况下,按照 Dave Cheney 的建议来定义调用者的接口将避免循环依赖。但这只会解决平面数据模型。在您的情况下,您有嵌套实体,即 HyperVisor 具有返回 MounthPath 的功能。我们可以通过两种方式对此进行建模在单独的包中定义 MouthPath(如您建议的那样)。此外,从长远来看,在 virtualbox 包中定义接口将有助于为 Hypervisor 提供替代实现。让 virtualbox 将 Hypervisor 和 MounthPath 都定义为接口。一个缺点是管理程序实现包使用 virtualbox.MouthPath 接口来满足如下传递时的接口。//hypervisor/hypervisor.gopackage hypervisortype Hypervisor struct{&nbsp; &nbsp; &nbsp;someField []virtualbox.MountPath}type MountPath struct { // this can be used as virtualbox.MountPath&nbsp; &nbsp; hostPath&nbsp; string&nbsp; &nbsp; guestPath string}func (m *MountPath) HostPath() string { return m.hostPath }func (m *MountPath) GuestPath() string { return m.guestPath }func detect() (Hypervisor, error) {&nbsp; &nbsp; return &virtualbox.Virtualbox{}, nil&nbsp; // <<1 HERE}并有另一个包(不需要嵌套)//hypervisor/virtualbox/vbox.gopackage virtualboxtype Hypervisor interface {&nbsp; &nbsp; Start(vmName string) error&nbsp; &nbsp; ListMounts(vmName string) ([]MountPath, error)&nbsp; &nbsp; //....}&nbsp;type MountPath interface {&nbsp; &nbsp; &nbsp; &nbsp; HostPath()&nbsp; string&nbsp; &nbsp; &nbsp; &nbsp; GuestPath() string}type Virtualbox struct {}func (*Virtualbox) Start(vmName string) error {&nbsp; &nbsp; return vboxManage("startvm", vmName, "--type", "headless").Run()}func (*Virtualbox) ListMounts(vmName string) ([]MountPath, error) { // <<2 HERE&nbsp; &nbsp; // ....}&nbsp;
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go