簡體   English   中英

在不同的包中有接口及其實現時如何管理循環依賴

[英]How to manage cyclic dependencies when have interface and its implementation in different packages

我的項目結構如下所示:

代碼結構:

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. hypervisor pcakge 引用virtualbox.VirtualBox類型
  2. 引用hypervisor.MountPath類型的virtualbox

我知道如果我將 struct MounthPath移動到另一個包會解決這個問題,但我認為這不是正確的解決方案設計。

有什么建議嗎?

我會做的最簡單的方法之一是將實體分離到entities包中(在這種情況下: HypervisorVirtualbox結構是實體或任何你想稱呼它的東西)。
這是我認為最常見的設計,因此內部包使用的每個結構都不會導致循環 deps。
用法示例:所有time包結構都在頂級包級別。 time.Time{}time.Duration{}time.Duration不屬於time/duration包。

在大多數情況下,遵循 Dave Cheney 的建議由調用者定義接口將避免循環依賴。 但這只能解決平面數據模型。 在您的情況下,您有嵌套實體,即 HyperVisor 具有返回 MounthPath 的功能。 我們可以通過兩種方式對此進行建模

  1. 在單獨的包中定義 MouthPath(如您所建議的)。 此外,在 virtualbox 包中定義接口將有助於為 Hypervisor 提供替代實現。

  2. 讓 virtualbox 將 Hypervisor 和 MounthPath 定義為接口。 一個缺點是管理程序實現包使用 virtualbox.MouthPath 接口來滿足如下傳遞時的接口。

//管理程序/管理程序.go

package hypervisor

type Hypervisor struct{
     someField []virtualbox.MountPath
}

type MountPath struct { // this can be used as virtualbox.MountPath
    hostPath  string
    guestPath string
}

func (m *MountPath) HostPath() string { return m.hostPath }
func (m *MountPath) GuestPath() string { return m.guestPath }

func detect() (Hypervisor, error) {
    return &virtualbox.Virtualbox{}, nil  // <<1 HERE
}

並有另一個包(不需要嵌套)

//hypervisor/virtualbox/vbox.go
package virtualbox

type Hypervisor interface {
    Start(vmName string) error

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

    //....
} 

type MountPath interface {
        HostPath()  string
        GuestPath() string
}

type Virtualbox struct {}

func (*Virtualbox) Start(vmName string) error {
    return vboxManage("startvm", vmName, "--type", "headless").Run()
}

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM