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标准库

5. go基础学习 coder python修行路

6.  可在https://godoc.org/中搜索所有golang库的接口说明。

7. 在 GitHub 上构建一个看上去正规的 Golang 项目

8. 如何写出优雅的 Go 语言代码

go工程组织规范的更多相关文章

  1. vue.js不仅是一种模式,也是一种工程组织方式

    vue.js不仅是一种模式,也是一种工程组织方式

  2. [golang note] 工程组织

    golang项目目录结构 <golang_proj> ├─README                 ├─AUTHORS                 ├─<bin>    ...

  3. JAVA工程命名规范

    Java推荐的包声明命名约定是反向域名. 例如 - com.abysm.myproject

  4. 建立QT工程的规范型,以及重要性

    当前管理开发多个项目,故名Projects 下一级目录,具体项目,故示例Project,根据实际情况自行取名 再下一级目录,有三个子目录 bin:生成的可执行文件或者动态链接库,build:编译源码时 ...

  5. 使用Yeoman generator来规范工程的初始化

    前言 随着开发团队不断发展壮大,在人员增加的同时也带来了协作成本的增加:业务项目越来越多,类型也各不相同.常见的类型有基础组件.业务组件.基于React的业务项目.基于Vue的业务项目等等.如果想要对 ...

  6. emacs工程管理,cedet ede插件自动构建Make,Automake

    鉴于自己一直都是在做客户端开发方面的工作,服务端很多知识都随着时间淡忘了,最近有一个计划,用一些时间补一下基础.所以早上很早就起床,花了一点时间大致浏览了一下BSD socket的相关API,然后用G ...

  7. java开发命名规范总结

    一 包名的书写规范 (Package)推荐使用公司或机构的顶级域名为包名的前缀,目的是保证各公司/机构内所使用的包名的唯一性.包名全部为小写字母,且具有实际的区分意义. 1.1 一般要求1.选择有意义 ...

  8. 标准的Java编码规范手册

    编码规范体现出一个开发者的基本素质,良好的编码规范可以提高团队编码的效率,避免很多不必要的问题.今天分享一个标准的Java编码规范给大家,希望对于大家今后的开发工作带来帮助. 编码规范的意义      ...

  9. python - 编程规范问题

    软件目录结构规范alex_老男孩:为什么要设计好目录结构?“设计项目目录结构”,就和“胆码编码风格”一样,属于个人风格问题.对于这种风格上的规范,一直都存在两种态度:    1.一类同学认为,这种个人 ...

随机推荐

  1. Angularjs 标签使用整理

    持续更新..... 一.select setmealList为接收到的集合数据,sname 是要显示的字段,Object属性 套餐类型:<select style="width: 15 ...

  2. OutOfMemoryError异常——Java堆溢出。

    https://blog.csdn.net/en_joker/article/details/79726975 (将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数- ...

  3. Oracle-关于Oracle.ManagedDataAccess

    今天调用webservice的时候,运行程序后开始报错以下的错误信息 “/”应用程序中的服务器错误. 未能加载文件或程序集“Oracle.DataAccess”或它的某一个依赖项.试图加载格式不正确的 ...

  4. Centos7挂载新硬盘

    1.查看系统是否检测到新的硬盘设备 ls /dev/ |grep sd linux 中所有外设都会在这个目录下,对应一个文件,其中第一块硬盘是sda,第二块硬盘是sdb,第三块硬盘是sdc.其中sda ...

  5. C++标准异常与自定义异常

    参见https://www.runoob.com/cplusplus/cpp-exceptions-handling.html C++ 标准的异常 C++ 提供了一系列标准的异常,定义在 中,我们可以 ...

  6. 如何申请百度地图用户Key

    打开网页http://lbsyun.baidu.com/,进入百度地图开发平台. 单击[登录],登录百度账号.如果您还没有百度账号,单击箭头处[立即注册]注册百度账号. 登录完成后,单击右上角箭头处[ ...

  7. day29——socket套接字(少量不全)

    day29 socket套接字 socket是处于应用层与传输层之间的抽象层,他是一组操作起来非常简单的接口(接受数据)此接口接受数据之后,交由操作系统. 为什么存在socket抽象层? 如果直接与操 ...

  8. centos7简单部署rancher

    rancher官网文档地址 https://www.cnrancher.com/docs/rancher/v2.x/cn/overview/ 准备机器 两台虚拟机 192.168.56.100 192 ...

  9. Dubbo快速入门 四

    4.业务场景 4.1).提出需求 某个电商系统,订单服务需要调用用户服务获取某个用户的所有地址: 我们现在 需要创建两个服务模块进行测试 模块 功能 订单服务web模块 创建订单等 用户服务servi ...

  10. [LOJ2292] [THUSC2016] 成绩单

    题目链接 LOJ:https://loj.ac/problem/2292 洛谷:https://www.luogu.org/problemnew/show/P5336 Solution 区间\(\rm ...