在Go语言中,不允许出现循环导入,这个原则真的是一个好的解决方案吗?

huangapple go评论95阅读模式
英文:

In golang, import cycle not allowed, is the principle really a good solution?

问题

我昨天在GitHub的问题中提问了这个问题,其中一位贡献者说我应该在这个网站上提问,所以我只是把我的问题粘贴到了stackoverflow上。

你使用的Go版本是什么(go version)?

$ go version
go version go1.17.1 windows/amd64

这个问题在最新版本中是否重现?

是的。

你使用的操作系统和处理器架构是什么(go env)?

$ go env

你做了什么?

我编写了一个简单的代码来尝试描述现实世界,但是会出现"Import cycle not allowed"的编译错误。所以我只能将简单的代码重写为复杂的代码。

"Import cycle not allowed"是一个正确的原则,但它不是使代码简单的原则,它使代码变得更加复杂。

这是一个临时原则还是一个永久原则?

它是在经过多次考虑和讨论后设计的,还是只是出于个人偏好?

我提出这个问题是因为我认为在现实世界中,一切都是自然相互作用的。

例如,一个老师进入教室,许多学生进入教室,然后老师选择学生A提问,然后学生B问老师。这是一个正常而简单的需求。

但是在Go语言中,下面的代码无法成功编译,因为不允许循环导入。这太奇怪了。

testgo
  main.go

├─classroom
      ClassRoom.go

├─student
      Student.go

└─teacher
        Teacher.go
// main.go
package main

import (
	"testgo/classroom"
	"testgo/student"
	"testgo/teacher"
)

func main() {
	c := &classroom.ClassRoom{}
	t := &teacher.Teacher{}
	a := &student.Student{}
	b := &student.Student{}
	t.Enter(c)
	a.Enter(c)
	b.Enter(c)
	t.Ask(a)
	b.Ask(t)
	print(c)
}
// classroom/ClassRoom.go
package classroom

import (
	"testgo/student"
	"testgo/teacher"
)

type ClassRoom struct {
	Teacher  *teacher.Teacher
	Students []*student.Student
}

func (c *ClassRoom) AddTeacher(t *teacher.Teacher) {
	c.Teacher = t
}

func (c *ClassRoom) AddStudent(s *student.Student) {
	c.Students = append(c.Students, s)
}
// teacher/Teacher.go
package teacher

import (
	"testgo/classroom"
	"testgo/student"
)

type Teacher struct {
	TeacherName string
	InClassRoom *classroom.ClassRoom
}

func (t *Teacher) Enter(c *classroom.ClassRoom) {
	c.AddTeacher(t)
	t.InClassRoom = c
}

func (t *Teacher) Ask(s *student.Student) {

}
// student/Student.go
package student

import (
	"testgo/classroom"
	"testgo/teacher"
)

type Student struct {
	StudentName string
	InClassRoom *classroom.ClassRoom
}

func (s *Student) Enter(c *classroom.ClassRoom) {
	c.AddStudent(s)
	s.InClassRoom = c
}

func (s *Student) Ask(t *teacher.Teacher) {

}

最后,会出现以下编译错误:

package testgo
	imports testgo/classroom
	imports testgo/student
	imports testgo/classroom: import cycle not allowed

还有更多的例子。

在现实世界中,猫捉住老鼠,老鼠逃离猫。package Cat 导入 package Ratpackage Rat 导入 package Cat

在现实世界中,动物吃水果,水果被动物吃掉。package Animal 导入 package Fruitpackage Fruit 导入 package Animal

在现实世界中,循环导入无处不在。

但是在Go语言中,不允许循环导入。

你期望看到什么?

允许循环导入,就像现实世界一样。

你看到了什么?

不允许循环导入。

英文:

I had ask it in github issue yesterday, and one of the contributors say that I should ask it at this site, so I just paste my question at stackoverflow.

<!--
Please answer these questions before submitting your issue. Thanks!
For questions please use one of our forums: https://github.com/golang/go/wiki/Questions
-->

What version of Go are you using (go version)?

<pre>
$ go version
go version go1.17.1 windows/amd64

</pre>

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env

</pre></details>

What did you do?

<!--
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.
-->

I write simple code to try to describe real world, but "Import cycle not allowed" will compile error. So I could only rewrite the simple code to complex code.

"Import cycle not allowed" is a right principle, but it is not a principle to makes code simple, it makes code more complex.

Is it a temporary principle or a permanent principle?

Is it designed after many consider and discuss, or only for personal preference?

I ask this question, because I think in real world, everything interacts with each other naturally.

For example, a teacher enter a classroom, many students enter a classroom, then teacher choose student a to ask, then the student b ask teacher. It is a normal simple requirement.

But in golang, the below code could not compile success, because import cycle not allowed. It is so strange.

testgo
│  main.go
│
├─classroom
│      ClassRoom.go
│
├─student
│      Student.go
│
└─teacher
        Teacher.go
// main.go
package main

import (
	&quot;testgo/classroom&quot;
	&quot;testgo/student&quot;
	&quot;testgo/teacher&quot;
)

func main() {
	c := &amp;classroom.ClassRoom{}
	t := &amp;teacher.Teacher{}
	a := &amp;student.Student{}
	b := &amp;student.Student{}
	t.Enter(c)
	a.Enter(c)
	b.Enter(c)
	t.Ask(a)
	b.Ask(t)
	print(c)
}
// classroom/ClassRoom.go
package classroom

import (
	&quot;testgo/student&quot;
	&quot;testgo/teacher&quot;
)

type ClassRoom struct {
	Teacher  *teacher.Teacher
	Students []*student.Student
}

func (c *ClassRoom) AddTeacher(t *teacher.Teacher) {
	c.Teacher = t
}

func (c *ClassRoom) AddStudent(s *student.Student) {
	c.Students = append(c.Students, s)
}
// teacher/Teacher.go
package teacher

import (
	&quot;testgo/classroom&quot;
	&quot;testgo/student&quot;
)

type Teacher struct {
	TeacherName string
	InClassRoom *classroom.ClassRoom
}

func (t *Teacher) Enter(c *classroom.ClassRoom) {
	c.AddTeacher(t)
	t.InClassRoom = c
}

func (t *Teacher) Ask(s *student.Student) {

}
// student/Student.go
package student

import (
	&quot;testgo/classroom&quot;
	&quot;testgo/teacher&quot;
)

type Student struct {
	StudentName string
	InClassRoom *classroom.ClassRoom
}

func (s *Student) Enter(c *classroom.ClassRoom) {
	c.AddStudent(s)
	s.InClassRoom = c
}

func (s *Student) Ask(t *teacher.Teacher) {

}

<font color="#dd0000"> Finally, it will compile error like below </font>

package testgo
	imports testgo/classroom
	imports testgo/student
	imports testgo/classroom: import cycle not allowed

There are more examples.

In real world, Cat catch Rat, Rat escape from Cat. package Cat import package Rat, and package Rat import package Cat.

In real world, Animal eat Fruit, Fruit be eat by Animal. package Animal import package Fruit, and package Fruit import package Animal.

In real world, import cycle occur everywhere.

But in golang, import cycle not allowed.

What did you expect to see?

enable import cycle, just like the real world

What did you see instead?

import cycle not allowed

答案1

得分: 2

这是一个永久的原则。循环依赖会增加编译时间,而Go语言非常注重快速编译。

解决循环依赖的方法是在一个新的包中引入一个新的接口。这个接口应该包含所有循环依赖结构体所具有的方法,并且可以被其他循环依赖的结构体访问。

我在这里找到了一个清晰的说明:https://medium.com/@ishagirdhar/import-cycles-in-golang-b467f9f0c5a0

英文:

It is a permanent principle. Circular dependencies increase compile time and GO is highly focused on fast compile time.

A solution to circular dependency is introducing a new interface in a new package. This interface should have all the methods that a circular dependent struct have and accessed by other circular dependent struct.

I found a clear notes about it https://medium.com/@ishagirdhar/import-cycles-in-golang-b467f9f0c5a0

答案2

得分: 1

这是正确且经过深思熟虑的原则。如果你理解并吸收了你的用例,就可以避免循环。例如:学生类应该只包含与学生相关的细节,如姓名、年龄、地址等等。班级应该有容量、地址等属性。还应该有一个名为schedule.go的单独类,用于保存班级、学生和教师之间的映射关系。

英文:

It is correct and well thought principal. If you understand and imbibe your use-case, you can avoid cycles. For eg:- student class should only have student related details, names, age, address etc etc. class should have things like capacity, address etc. There should be a separate class named schedule.go which would hold the mapping of class, student and teacher.

huangapple
  • 本文由 发表于 2021年11月1日 01:59:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/69789216.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定