本文同時發布個人CSDN部落格: https://blog.csdn.net/ggq89/article/details/81148558
相信不少 Gopher
在寫 Golang
程式都遇到過 import cycle not allowed
問題,本人最近研讀 go-ethereum 源碼時,發現定義 interface
也能解決此問題, 還能解決連分包都不能解決的情況, 並且比分包更加簡單快捷。下面逐個講解 分包
和 定義介面
這兩種方法。
1. 應用情境
假設有如下使用情境:
A
是應用程式的架構級結構體,在 A
包含子模組 B
和 C
的指標;
B
為了方便的使用應用的其他子模組(比如 C
)功能,所以在其結構體包含了 A
的指標;
C
要調用 A
包中的某個方法;
2. 代碼實現
其程式大致如下:
package aimport ( "fmt" "github.com/ggq89/mutualdep/b" "github.com/ggq89/mutualdep/c")type A struct { Pb *b.B Pc *c.C}func New(ic int) *A { a := &A{ Pc: c.New(ic), } a.Pb = b.New(a) return a}func Printf(v int) { fmt.Printf("%v", v)}
package bimport ( "github.com/ggq89/mutualdep/a")type B struct { Pa *a.A}func New(a *a.A) *B { return &B{ Pa: a, }}func (b *B) DisplayC() { b.Pa.Pc.Show()}
package cimport "github.com/ggq89/mutualdep/a"type C struct { Vc int}func New(i int) *C { return &C{ Vc: i, }}func (c *C) Show() { a.Printf(c.Vc)}
package a
依賴 package b
和 package c
,同時 package b
依賴 package a
、 package c
也依賴 package a
。
main
函數代碼如下:
package mainimport "github.com/ggq89/mutualdep/a"func main() { a := a.New(3) a.Pb.DisplayC()}
編譯時間就會報錯如下:
import cycle not allowedpackage main imports github.com/ggq89/mutualdep/a imports github.com/ggq89/mutualdep/b imports github.com/ggq89/mutualdep/a
3. 定義介面
現在的問題是:
A depends on B B depends on A
對於 A struct
和 B struct
有彼此的指標這種相互依賴問題,可以使用定義介面的方法解決,具體步驟如下:
- 在
package b
中 定義 a interface
; 將 b
所有使用到結構體 a
的變數和方法的地方全部轉化成 使用介面 a
的方法;在 a interface
中補充缺少的方法;
經過上面的步驟處理後, package b
代碼如下:
package bimport ( "github.com/ggq89/mutualdep/c")type B struct { Pa a}type a interface { GetC() *c.C}func New(a a) *B { return &B{ Pa:a, }}func (b *B) DisplayC() { b.Pa.GetC().Show()}
處理後, package a
中的代碼如下:
package aimport ( "fmt" "github.com/ggq89/mutualdep/b" "github.com/ggq89/mutualdep/c")type A struct { Pb *b.B Pc *c.C}func New(ic int) *A { a := &A{ Pc:c.New(ic), } a.Pb = b.New(a) return a}func (a *A)GetC() *c.C { return a.Pc}func Printf(v int) { fmt.Printf("%v", v)}
4. 拆分包
再次編譯,提示如下:
import cycle not allowedpackage main imports github.com/ggq89/mutualdep/a imports github.com/ggq89/mutualdep/b imports github.com/ggq89/mutualdep/c imports github.com/ggq89/mutualdep/a
現在是另一個相互依賴問題:
A depends on C C depends on A
與前面的相互依賴不同,前面的依賴是由於 A struct
和 B struct
有彼此的指標導致的,屬於硬相互依賴;
而這裡是由於 package c
中的方法調用 package a
中的方法引起的,屬於軟相互依賴;
- 這種相互依賴可以通過將方法拆分到另一個包的方式來解決;在拆分包的過程中,可能會將結構體的方法轉化為普通的函數;
引入 package f
, 將方法遷移到 f
中 :
package fimport "fmt"func Printf(v int) { fmt.Printf("%v", v)}
方法移動到 package f
後, package a
的代碼如下:
package aimport ( "github.com/ggq89/mutualdep/b" "github.com/ggq89/mutualdep/c")type A struct { Pb *b.B Pc *c.C}func New(ic int) *A { a := &A{ Pc: c.New(ic), } a.Pb = b.New(a) return a}func (a *A) GetC() *c.C { return a.Pc}
package c
隨之改成調用package f
,其代碼如下:
package cimport ( "github.com/ggq89/mutualdep/a/f")type C struct { Vc int}func New(i int) *C { return &C{ Vc: i, }}func (c *C) Show() { f.Printf(c.Vc)}
現在依賴關係如下:
A depends on B and CB depends on CC depends on F
至此,兩種包相互依賴關係都得以解決。
5. 總結
對於軟相互依賴,利用分包的方法就能解決,有些函數導致的相互依賴只能通過分包解決;分包能細化包的功能;
對於硬相互依賴只能通過定義介面的方法解決;定義介面能提高包的獨立性,同時也提高了追蹤代碼調用關係的難度;
參考文章:
- golang不允許迴圈import問題("import cycle not allowed") : http://ju.outofmemory.cn/entry/230115
- golang解決import cycle not allowed的一種思路 : https://studygolang.com/articles/10582?fr=sidebar
- golang中("import cycle not allowed")錯誤 : https://blog.csdn.net/skh2015java/article/details/53943784