go工程组织规范
go编码以workspace形式管理,一个workspace包含所有的Go编码,包含多个版本控制仓库(例如使用git管理的多个仓库)。每个仓库包含多个包package,每个package是一个单独的路径,包含所有go源码,包的路径就是包的导入路径(import path)。
1. Workspace
Workspace作为根目录,包含两个目录src和bin。bin包含可执行文件,src包含源码。典型的src包含多个版本控制仓库(记录源码开发过程)。形如:
上面的workspace包含两个仓库example和image。example包含两个命令(或包,hello和outyet)和一个库(或包,stringutil)。
$GOPATH指向当前的Workspace。
包的导入路径(import path),指定一个包在workspace或远程仓库的位置。标准库的导入路径可以简写,如“fmt”和“net/http”。但自有库的包的导入路径应该是绝对路径(src下级路径开始),包含基础路径(如:github.com/user)。可通过go help importpath获取更多导入路径的信息。
go源码的第一有效行(注释行除外)必须是
package name
name是导入路径(import path)的默认包名。在一个包中所有文件必须用相同包名,惯例:导入路径名的最后一项应与包名相同,以方便识别和记忆,不出错。
一个程序只有一个main包,一个包中只能有一个main函数,不能重复定义。
对一个可执行程序,不要求package名唯一,但要求导入路径唯一(即可明确确认一个包)。
go程序导出名字
在 Go 中,任何以大写字母开头的变量或者函数都是被导出的名字。其它包只能访问被导出的函数和变量。
同一个包中,所有变量和函数都可调用,无论首字母是否大小写。
first程序
用自己的github账号创建仓库路径:$GOPATH/src/github.com/yuxi-o/golang,此目录下创建包目录hello,然后在hello目录下创建hello.go程序。
目前hello.go路径为:src/github.com/yuxi-o/golang/hello/hello.go。
现在运行如下命令安装hello.go程序:
$go install github.com/yuxi-o/golang/hello
注意:go install或go build执行的路径为package路径,可执行程序名为包路径名。
可采用git管理yuxi-o/golang仓库,在golang目录下:
~/go/src/github.com/yuxi-o/golang$ ls
hello
~/go/src/github.com/yuxi-o/golang$ git init
Initialized empty Git repository in /home/wang/go/src/github.com/yuxi-o/golang/.git/
~/go/src/github.com/yuxi-o/golang$ git add .
~/go/src/github.com/yuxi-o/golang$ git commit -m "first go program"
[master (root-commit) bd2635a] first go program
file changed, insertions(+)
create mode hello/hello.go
first库
mkdir $GOPATH/src/github.com/yuxi-o/golang/stringutil
cat ~/go/src/github.com/yuxi-o/golang/stringutil/reverse.go
package stringutil func Reverse(s string) string {
r := []rune(s)
for i,j := , len(r)-; i< len(r)/; i,j= i+, j- {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
$ go build github.com/yuxi-o/golang/stringutil
注:go build编译库不会输出文件,保存编译包到local build cache。
cat ~/go/src/github.com/yuxi-o/golang/hello/hello.go
package main
import (
"fmt"
"github.com/yuxi-o/golang/stringutil"
) func main() {
// fmt.Println("Hello, Go world")
fmt.Println(stringutil.Reverse("!oG, olleH"))
}
$go install github.com/yuxi-o/golang/hello
现在目录如下:
go测试
go提供轻量级测试框架,包含go test和testing package。
go测试文件命令为_test.go,测试函数定义:func TestXXX(t *testing.T)。test框架运行每个测试函数。假如测试失败,返回t.Error或t.Fail。
cat ~/go/src/github.com/yuxi-o/golang/stringutil/reverse_test.go
package stringutil
import "testing" func TestReverse(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := Reverse(c.in)
if got != c.want {
t.Errorf("Reverse(%q) == %q, wang %q", c.in, got, c.want)
}
}
}
$ go test github.com/yuxi-o/golang/stringutil
ok github.com/yuxi-o/golang/stringutil .002s
Remote包
导入路径可以描述怎样获取package源码(通过git或mercurial)。go get可以fetch、build和install包,若包不存在,go get会下载包到第一个workspace。可下载golang/example测试:
$ go get github.com/golang/example/hello
$ $GOPATH/bin/hello
Hello, Go examples!
2. go项目结构
go项目体检的目录结构:golang-standards/project-layout
├── LICENSE.md
├── Makefile
├── README.md
├── api
├── assets
├── build
├── cmd
├── configs
├── deployments
├── docs
├── examples
├── githooks
├── init
├── internal
├── pkg
├── scripts
├── test
├── third_party
├── tools
├── vendor
├── web
└── website
/pkg
这个目录中存放的就是项目中可以被外部应用使用的代码库,其他的项目可以直接通过 import
引入这里的代码,所以当我们将代码放入 pkg
时一定要慎重,建议各位开发者对项目中公有和私有的代码进行妥善的划分。
/internal
私有代码推荐放到 /internal
目录中,真正的项目代码应该写在 /internal/app
里,同时这些内部应用依赖的代码库应该在 /internal/pkg
子目录和 /pkg
中,下图展示了一个使用 /internal
目录的项目结构:
当我们在其他项目引入包含 internal
的依赖时,Go 语言会在编译时报错:
An import of a path containing the element “internal” is disallowed
if the importing code is outside the tree rooted at the parent of the "internal" directory.
这种错误只有在被引入的 internal
包不存在于当前项目树中才会发生,如果在同一个项目中引入该项目的 internal
包并不会出现这种错误。
/src
在 Go 语言的项目最不应该有的目录结构其实就是 /src
了,社区中的一些项目确实有 /src
文件夹,但是这些项目的开发者之前大多数都有 Java 的编程经验,这在 Java 和其他语言中其实是一个比较常见的代码组织方式,但是作为一个 Go 语言的开发者,我们不应该允许项目中存在 /src
目录。
最重要的原因其实是 Go 语言的项目在默认情况下都会被放置到 $GOPATH/src
目录下,这个目录中存储着我们开发和依赖的全部项目代码,如果我们在自己的项目中使用 /src
目录,该项目的 PATH
中就会出现两个 src
:
$GOPATH/src/github.com/draveness/project/src/code.go
上面的目录结构看起来非常奇怪,这也是我们在 Go 语言中不建议使用 /src
目录的最重要原因。
平铺
另一种在 Go 语言中组织代码的方式就是项目的根目录下放项目的代码,这种方式在很多框架或者库中非常常见,如果想要引入一个使用 pkg
目录结构的框架时,我们往往需要使用 github.com/draveness/project/pkg/somepkg
,当代码都平铺在项目的根目录时只需要使用 github.com/draveness/project
,很明显地减少了引用依赖包语句的长度。
所以对于一个 Go 语言的框架或者库,将代码平铺在根目录下也很正常,但是在一个 Go 语言的服务中使用这种代码组织方法可能就没有那么合适了。
/cmd
/cmd
目录中存储的都是当前项目中的可执行文件,该目录下的每一个子目录都应该包含我们希望有的可执行文件,如果我们的项目是一个 grpc
服务的话,可能在 /cmd/server/main.go
中就包含了启动服务进程的代码,编译后生成的可执行文件就是 server
。
我们不应该在 /cmd
目录中放置太多的代码,我们应该将公有代码放置到 /pkg
中并将私有代码放置到 /internal
中并在 /cmd
中引入这些包,保证 main
函数中的代码尽可能简单和少。
/api
/api
目录中存放的就是当前项目对外提供的各种不同类型的 API 接口定义文件了,其中可能包含类似 /api/protobuf-spec
、/api/thrift-spec
或者 /api/http-spec
的目录,这些目录中包含了当前项目对外提供的和依赖的所有 API 文件:
$ tree ./api
api
└── protobuf-spec
└── oceanbookpb
├── oceanbook.pb.go
└── oceanbook.proto
二级目录的主要作用就是在一个项目同时提供了多种不同的访问方式时,用这种办法避免可能存在的潜在冲突问题,也可以让项目结构的组织更加清晰。
Makefile
最后要介绍的 Makefile
文件也非常值得被关注,在任何一个项目中都会存在一些需要运行的脚本,这些脚本文件应该被放到 /scripts
目录中并由 Makefile
触发,将这些经常需要运行的命令固化成脚本减少『祖传命令』的出现。
参考:
1. How to write Go Code https://golang.google.cn/doc/code.html
2. https://golang.google.cn/ 提供go在线测试环境和文档
3. https://golang.google.cn/doc/ go相关文档
4. https://golang.google.cn/pkg/ go标准库
6. 可在https://godoc.org/中搜索所有golang库的接口说明。
7. 在 GitHub 上构建一个看上去正规的 Golang 项目
go工程组织规范的更多相关文章
- vue.js不仅是一种模式,也是一种工程组织方式
vue.js不仅是一种模式,也是一种工程组织方式
- [golang note] 工程组织
golang项目目录结构 <golang_proj> ├─README ├─AUTHORS ├─<bin> ...
- JAVA工程命名规范
Java推荐的包声明命名约定是反向域名. 例如 - com.abysm.myproject
- 建立QT工程的规范型,以及重要性
当前管理开发多个项目,故名Projects 下一级目录,具体项目,故示例Project,根据实际情况自行取名 再下一级目录,有三个子目录 bin:生成的可执行文件或者动态链接库,build:编译源码时 ...
- 使用Yeoman generator来规范工程的初始化
前言 随着开发团队不断发展壮大,在人员增加的同时也带来了协作成本的增加:业务项目越来越多,类型也各不相同.常见的类型有基础组件.业务组件.基于React的业务项目.基于Vue的业务项目等等.如果想要对 ...
- emacs工程管理,cedet ede插件自动构建Make,Automake
鉴于自己一直都是在做客户端开发方面的工作,服务端很多知识都随着时间淡忘了,最近有一个计划,用一些时间补一下基础.所以早上很早就起床,花了一点时间大致浏览了一下BSD socket的相关API,然后用G ...
- java开发命名规范总结
一 包名的书写规范 (Package)推荐使用公司或机构的顶级域名为包名的前缀,目的是保证各公司/机构内所使用的包名的唯一性.包名全部为小写字母,且具有实际的区分意义. 1.1 一般要求1.选择有意义 ...
- 标准的Java编码规范手册
编码规范体现出一个开发者的基本素质,良好的编码规范可以提高团队编码的效率,避免很多不必要的问题.今天分享一个标准的Java编码规范给大家,希望对于大家今后的开发工作带来帮助. 编码规范的意义 ...
- python - 编程规范问题
软件目录结构规范alex_老男孩:为什么要设计好目录结构?“设计项目目录结构”,就和“胆码编码风格”一样,属于个人风格问题.对于这种风格上的规范,一直都存在两种态度: 1.一类同学认为,这种个人 ...
随机推荐
- C++11:基于std::queue和std::mutex构建一个线程安全的队列
C++11:基于std::queue和std::mutex构建一个线程安全的队列 C++中的模板std::queue提供了一个队列容器,但这个容器并不是线程安全的,如果在多线程环境下使用队列,它是不能 ...
- .net程序反编译工具(ILSpy)
ILSpy是SharpDevelop小组的反编译工具,ILSPY这个开源工具的目的就是代替reflector的,它可以反编译出比reflector更好的C#代码. PC官方版 C#反编译工具ilspy ...
- Python 初级 6 循环 (三)
一.复习 1 计算循环(for循环) for looper in [1, 2, 3, 4, 5]: print("hello") 1) looper的值从第0个数1开始 2) 对应 ...
- 深入分析GCC
深入分析GCC 目录 前言章 GCC概述 11.1 GCC的产生与发展 11.2 GCC的特点 21.3 GCC代码分析 3第2章 GCC源代码分析工具 42.1 vim ctags代码阅读工具 42 ...
- 浏览器中开发人员工具快速找到dom元素绑定那些JS事件
在web开发过程中难免会遇到让程序员去修改一些js代码东西,例如js的ajax和php等语言的交互等,在这其中你不得不了解点js的事件触发,且随着js的盛行各种插件的事件让程序员眼花缭乱,所以借助一个 ...
- C#反射技术的简单操作(读取和设置类的属性、属性值)
public class A { public int Property1 { get; set; } } static void Main(){ A aa = new A(); Type type ...
- .NET(C#)有哪些主流的ORM框架,SqlSugar,Dapper,EF还是...
前言 在以前的一篇文章中,为大家分享了<什么是ORM?为什么用ORM?浅析ORM的使用及利弊>.那么,在目前的.NET(C#)的世界里,有哪些主流的ORM,SqlSugar,Dapper, ...
- UE4 ios环境搭建备忘
1.windows.mac安装证书 2.安装xcode .app -- 路径可以拖入 sudo gem install xcodeproj 3.错误处理 Setting up Mono Running ...
- Influx Sql系列教程一:database 数据库
对于influxdb而言,database和我们更熟悉的mysql中的dababse没有什么特别的区别,可以将数据库简单理解为一堆表(measurement)的集合,接下来我们将看一下在influxd ...
- 使用WinFrom + CefSharp 开发客户端程序
今天使用CefSharp,加上本地资源文件嵌入了HTML.CSS.JS文件,做为Winform的UI:效果不错,漂亮可控,简简单单,半天时间搞定从开发到上线: 下面记录下相关的备忘: (窗口的效果) ...