英文:
How should I organise structs, variables and interfaces in Go?
问题
我有一个代码库,其中一个文件包含了很多Structs
、Interfaces
和Variables
,这些内容与函数放在同一个文件中。我不确定是否需要将它们分成不同的文件,并在文件名后添加后缀。例如,accounts.go
将变为accounts_struct.go
和accounts_interface.go
,分别用于存放结构体和接口。
当代码库中的结构体、变量和接口不断增长时,一个好的文件组织方法是什么?
英文:
I have a codebase where one file contains quite a lot of Structs
, Interfaces
and Variables
in the same file as functions and I'm not sure if I need to seperate this into separate files with appending filename. So for example accounts.go
will be accounts_struct.go
and accounts_interface.go
with struct and interface respectively.
What would be a good approach for the file organisation when you have growing codebase for Structs, Variables and Interfaces?
答案1
得分: 27
一个很好的模型可以参考Go语言本身的源代码:http://golang.org/src(除了官方的《Effective Go》之外)。
你会发现这种按照语言项(如结构体、接口等)进行分离的方法从未被使用。
所有的文件都是基于功能进行组织的,最好采用接近原则的方法,可以在同一个文件中找到你正在使用的定义。通常,这些功能会按照每个包一个文件的方式进行分组,除了一些较大的功能,其中一个包由多个文件组成(例如net
,net/http
)。
如果你想要进行分离,可以将源代码(xxx.go
)与测试/基准测试(xxx_test.go
)分开。
正如Thomas Jay Rush在评论中所补充的:
> 有时候源代码是自动生成的,特别是数据结构定义。如果数据结构与手工编写的代码在同一个文件中,就必须在代码生成阶段保留手工编写部分的能力。如果将数据结构分离到不同的文件中,那么通过包含就可以简单地编写数据结构文件而不用担心其他问题。
Dave Cheney在《Absolute Unit (Test) @ LondonGophers》(2019年3月)中提供了一个有趣的观点。
> 你应该对要测试的“单元”有一个更广泛的视角。这些单元不是你编写的每个内部函数,而是一个完整的包。具体来说,是一个包的公共API。
组织你的文件以便测试它们的公共API是一个好主意。在这方面,accounts_struct_test.go
并没有太多意义。
还可以参考**Bartłomiej Klimczak**的文章《How I organize packages in Go》。
> 有时候需要几个处理程序或存储库。例如,一些信息可以存储在数据库中,然后通过事件发送到平台的其他部分。只保留一个具有saveToDb()
方法的存储库并不那么方便。所有这些元素都按功能进行拆分:repository_order.go
或service_user.go
。如果有超过3种类型的对象,它们将被移动到单独的子文件夹中。
英文:
A good model to check out is the source code of Go itself: http://golang.org/src
(in addition of the official "Effective Go")
You will see that this approach (separating based on language items like struct, interface, ...) is never used.
All the files are based on features, and it is best to use a proximity principle approach, where you can find in the same file the definition of what you are using.
Generally, those features are grouped in one file per package, except for large ones, where one package is composed of many files (net
, net/http
)
If you want to separate anything, separate the source (xxx.go
) from the tests/benchmarks (xxx_test.go
)
As Thomas Jay Rush adds in the comments
> Sometimes source code is automatically generated -- especially data structure definitions.
If the data structures are in the same file as the hand-wrought code, one must build capacity to preserve the hand-wrought portion in the code-generation phase.
If the data structures are separated in a different file, then inclusion allows one to simply write the data structure file without worry.
Dave Cheney offers an interesting perspective in "Absolute Unit (Test) @ LondonGophers" (March 2019)
> You should take a broader view of the "unit" under test.
The units are not each internal function you write, but a whole package. Specifically the public API of a package.
Organizing your files to facilitate testing their Public API is a good idea.
accounts_struct_test.go
would not, in that regards, make much sense.
See also "How I organize packages in Go" by Bartłomiej Klimczak
> Sometimes, a few handlers or repositories are needed.
For example, some information can be stored in a database and then sent via an event to a different part of your platform. Keeping only one repository with a method like saveToDb()
isn’t that handy at all.
All of elements like that are split by the functionality: repository_order.go
or service_user.go
.
If there are more than 3 types of the object, there are moved to a separate subfolder.
答案2
得分: 24
这是我设计软件包的思维模型。
a. 一个软件包应该包含一个想法或概念。http 是一个概念,http 客户端或http 消息不是。
b. 一个软件包中的文件应该包含一组相关的类型,一个好的经验法则是,如果两个文件共享相同的导入集合,就将它们合并。以前面的例子为例,http/client.go
,http/server.go
是一个很好的粒度。
c. 不要为每个类型创建一个文件,这不符合 Go 的惯用方式。
英文:
Here is my mental model for designing a package.
a. A package should encompass one idea or concept. http is a concept, http client or http message is not.
b. A file in a package should encompass a set of related types, a good rule of thumb is if two files share the same set of imports, merge them. Using the previous example, http/client.go
, http/server.go
are a good level of granularity
c. Don't do one file per type, that's not idiomatic Go.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论