周期性进口和缺乏仿制药引起头痛

Say I have these two files in golang:

// main/a/a.go
import "main/b"

type Model struct {
    ID         int    `json:"id"`
    Me         int    `json:"me"`
    You        int    `json:"you"`
}

func zoom(v b.Injection){

}

func Start(){
  // ...
}

and then the second file looks like:

// main/b/b.go
import "main/a"

type Injection struct {
    ModelA a.Model
}


func GetInjection() Injection {
    return Injection{
        ModelA: a.Start(),
    }
}

so as you can see, these are circular imports, each file imports the other. So I need to use a 3rd file, and have these two files import the 3rd file.

But I am really struggling how to get this functionality and avoid cyclic imports.

My first step, is to move the Injection type into a 3rd file:

   // main/c/c.go

    type Injection struct {
        ModelA interface{} // formerly a.Model
    }

so now this is what it looks like:

a imports c
b imports a,c

so no more cycles, however the problem is that I don't know how to create an interface for a.Model in c.go? An empty interface{} like I used above doesn't work, for the normal reasons.

How do I solve this cyclic import problem with these 2 original files?

If you want them to put into separate packages, you can't have Model and zoom() in the same package, as zoom() refers to Injection and Injection refers to Model.

So a possible solution is to put Model into package a, zoom() into package b, and Injection into package c. c.Injection can refer to a.Model, b.zoom() can refer to c.Injection. There's no circle in this:

   b.zoom() -------->   c.Injection ---------> a.Model

I assume there are other references in your real code which are not in the question which may prevent this from working, but you can move "stuff" around between packages, or you can break it down into more.

Also, if things are coupled this "tight", you should really consider putting them into the same package, and then there is no problem to solve.

Another way to solve circular import issue is to introduce interfaces. E.g. if your zoom() function would not refer to Injection, the package containing Model and zoom() would not need to refer to Injection's package.

Inspect what zoom() needs to do with Injection. If that is method calls, that's already good. If not, add methods to Injection. Then you may define an interface in zoom()'s package containing the methods zoom() needs to call, and change its parameter type to this interface. Implementing interfaces in Go is implicit, there is no declaration of intent. So you can remove the reference in the parameter type, still you will be able to pass Injection values to zoom().

Also related, check Dave Cheney's thoughts about organizing code:

I believe code should be organised into packages names for what the package provides, not what it contains. This can sometimes be subtle, but usually not.

For example, http, provides http clients and servers.

As a counter example, package utils is a poor name, yes it provides utilities, but you have no idea what from the name, in truth this package is named for what it contains.

If your project is a library, it should contain one package (excluding examples and possibly utility commands), if it contains more packages, that is a sign that the library is trying to do too many things.

Prefer to avoid multiple packages by default, only split code by package if there is a clear separation of concerns. In my experience many frustrations with complex and possibly circular package structures are the result of too many packages in a project.