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. svn密码找回

    TortoiseSVN Password Decrypterhttp://www.leapbeyond.com/ric/TSvnPD/点击打开链接去这个地址下载这个,然后直接运行,就可以看到你的use ...

  2. (转)面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题

    背景:redis问题在面试过程中经常被问到,对于常见问题一定不能放过. 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题 一.缓存雪崩 1.1什么是缓存雪崩? 如果缓存数据设置的过 ...

  3. jira中使用eazyBI

    参考:https://docs.eazybi.com/eazybijira/set-up-and-administer/set-up-and-administer-for-jira-server/in ...

  4. A+B for Polynomials

    This time, you are supposed to find A+B where A and B are two polynomials. Input Specification: Each ...

  5. Mahalanobia Distance(马氏距离)的解释

    马氏距离有多重定义: 1)可以表示 某一个样本与DataSet的距离. 2)可以表示两个DataSet之间的距离. 1) The Mahalanobis distance of an observat ...

  6. 罗辑思维首席架构师:Go微服务改造实践

    转自:http://www.infoq.com/cn/news/2018/05/luojisiwei 方圆 曾先后在 Cisco,新浪微博从事基础架构研发工作.十多年一直专注于后端技术的研发,在消息通 ...

  7. [转帖]B树索引、位图索引和散列索引

    B树索引.位图索引和散列索引   https://blog.csdn.net/huashanlunjian/article/details/84460436 索引在数据结构上可以分为三种B树索引.位图 ...

  8. 下载 m3u8 直播流的方法

    下载 FFmpeg http://ffmpeg.org/download.html 查找直播流地址 找到目标视频对应的 m3u8 播放列表. 执行脚本 ffmpeg -i https://nhkmov ...

  9. UML概念模型

    UML概念模型 UML(Unified Modeling Language):统一建模语言,为面向对象开发系统的产品进行说明.可视化.和编制文档的标准语言 面向对象程序设计 面向对象基本概念:对象.类 ...

  10. vue+element+upload实现头像上传

    后台 @RequestMapping("/up") public JSONObject up(@RequestParam("picFile") Multipar ...