前言

  gopher原意地鼠,在golang 的世界里解释为地道的go程序员。在其他语言的世界里也有PHPer,Pythonic的说法,反而Java是个例外。虽然也有Javaer之类的说法,但似乎并不被认可。而地道或者说道地,说的是gopher写的代码无不透露出go的独特气息,比如项目结构、命名方式、代码格式、编码风格、构建方式等等。用gopher的话说,用go编写代码就像是在画一幅中国山水画,成品美不胜收,心旷神怡。

环境变量

  gopher第一条:把东西放对地方。

  go程序的运行,需要依赖于两个基础的环境变量,GOROOT与GOPATH。环境变量几乎在各类编程语言中都存在,比如java的JAVA_HOME,其实也就是编译器及相关工具或标准库所在目录。但go除了GOROOT之外,还增加了GOPATH,它指的是go程序依赖的第三方库或自有库所在目录,以指示编译器从这些地方找到依赖。GOPATH支持多个目录,通常一个目录就是一个项目,并且GOPATH目录按约定由src、pkg、bin三个目录组成。gopher们的做法是定义Global GOPATH、Project GOPATH,而更大的项目还会定义Module GOPATH。当使用go get下载依赖时,会选择GOPATH环境变量中的第一个目录存放依赖包。

变量 含义 说明
GOROOT go运行环境根目录 通常指go sdk安装目录,包含编译器、官方工具及标准库
GOPATH 工作环境目录列表 通常指第三方库、自有库及项目目录
GOPATH
  1. src: 源代码目录
  2. pkg: 编译时中间文件所在目录
  3. bin: 编译后生成可执行文件所在目录
├── src
| └── eventer.com
| └── test
| └── main.go
├── pkg
└── bin
| └── test.bin
Global GOPATH

  全局GOPATH,通常将第三方库的代码保存至此处,便于所有项目引用,而不用重复下载。因此在GOPATH中,建议将global gopath放在最前面。

Project GOPATH

  如果项目比较简单的话,可以采用global GOPATH与project GOPATH的组合,即GOPATH设置为global GOPATH与project OGPATH两个目录。比如引用了需要对源码稍作修改的开源项目时,开源项目可以跟当前项目一起放在src目录下。

Module GOPATH

  在比较复杂的项目中,如果划分的模块比较多,则可以采用global, project, module三种GOPATH组合的方式,即即GOPATH设置为global GOPATH、project OGPATH、module GOPATH三个目录。

  最后请注意,global GOPATH、project GOPATH、module GOPATH不是go语言中的概念,go要求的只有GOPATH。而因为GOPATH是一个目录列表,所以我只是在实践中将GOPATH细分为上述三者,同时便于讲述GOPATH的概念。

项目结构

  gopher第二条:按东西放在约定的地方。

  不同复杂度的项目,大致可以有两种类型的项目结构,第一种是依赖较少、较简单的项目,如下图所示,其中mydriver项目是假设要引用的开源项目,但要在当前项目的使用中稍修改,因此与当前项目的源码放在一起。

├── src
| ├── github.com
| └── eventer
| └── mydriver
| └── driver.go
| └── eventer.com
| └── test
| └── main.go
├── pkg
└── bin

  第二种是依赖较多、模块划分较细、较复杂的项目,如下图所示:

├── src
| └── eventer.com
| └── test
| └── main.go
├── pkg
├── bin
├── module1
| ├── src
| └── eventer.com
| └── lib
| └── lib1.go
| ├── pkg
| └── bin
├── module2
| ├── src
| └── eventer.com
| └── lib
| └── lib2.go
| ├── pkg
| └── bin
└── module3
| ├── src
| └── eventer.com
| └── lib
| └── lib3.go
| ├── pkg
| └── bin

  其中的module1,module2,module3是基础模块,被主项目依赖。这个时候除了将主项目路径放在GOPATH外,还应当将基础模块路径放在GOPATH中。这样的项目结构不仅让项目清晰,还可以做不同的分工。

命名规范

  gopher第三条:把名字起得go一点。

  go语言的命名与其他语言最大的不同在于首字母的大小写。大写代表公开(导出,可以在其他包内访问);小写代表私有(不导出,只能在包内访问)。除此之外,与其他语言并无二致,比如不能以数字开头。而由于关键字、保留字的减少,因而减少了一些命名上的忌讳。更为突出的是,go语言有一些建议性的命名规范,这也是gophers的圣经,理应严格遵守。

约定 范围 说明 示例
驼峰命名法 全局 统一使用驼峰命名法 var isLive = false
大小写一致 缩写短语,惯用词 如HTML,CSS, HTTP等 htmlEscape,HTMLEscape
简短命名法 局部变量 方法内、循环等使用的局部变量可以使用简短命名 比如for循环中的i,buf代表buffer等
参数命名法 函数参数、返回值、方法接收者 如果参数类型能说明含义,则参数名可以简短,否则应该采用有文档说明能力的命名 比如d Duration,t Time
通用命名法 作用域越大或者使用的地方离声明的地方太远,则应采用清晰有意义的命名 -
导出命名法 导出变量、函数、结构等 包名与导出名意义不要重复,同时包的命名要与导出的内容相关,不要使用宽泛的名字,如common,util bytes.Buffer比bytes.ByteBuffer要好
文件命名 go文件,单元测试文件 go文件名尽量以一个单词来命名,多个单词使用下线划分隔,单元测试文件以对应go文件名加_test结尾 proto_test
包命名 包的一级名称应是顶级域名,二级名称则应是项目名称,项目名称单词间以-分隔 github.com/mysql

代码格式

  gopher第四条:按统一的格式来。

  在多人协作团队中,统一的代码格式化模板是第一要义。在Java语言中,检验新人经验的一大法宝就是他有没有主动索要代码模板。而在go语言中,则没有这个必要了。因为go已经有默认的代码格式化工具了,而且代码格式化在go语言中是强制规范。所以这使得所有go程序员写出来的代码格式都是一样的。

  go默认的代码格式化工具是gofmt。另外还有一个增强工具goimport,在gofmt的基础上增加了自动删除和引入依赖包。而行长则以不超过80个字符为佳,超过请主动以多行展示。

编码风格

  gopher第五条:请学会约定

约定 说明
import 按标准库、内部包、第三方包的顺序导入包 只引一个包时使用单行模式,否则使用多行模式
变量声明 如果连续声明多个变量,应放在一起 参见例子
错误处理 不要忽略每一个error,即使只是打一行日志 go的error处理方式与C同出一辙,通过返回值来标明错误或异常,引来的争议也很多,甚至官方已经开始酝酿在go2解决这个问题
长语句打印 使用格式化方式打印 -
注释规范 变量、方法、结构等的注释直接加上声明前,并且不要加空行。废弃方法加Deprecated:即可 其中的第一行注释会被godoc识别为简短介绍,第二行开始则被认为是注释详情。注释对godoc的生成至关重要,因此关于注释会有一些技巧,我将在后面用专门的章节探讨

多变量声明

var (
name string
age int
)

注释规范

// Add 两数相加
// 两个整数相加,并返回和。
func Add(n1, n2 int)int{
return n1 + n2
}

依赖管理

  gopher第六条:使用依赖管理工具管理自有依赖与第三方依赖

  一个语言的生态是否完善甚至是否强大,除了github上面的开源项目数量之外,还有一大特征就是是否有优秀的依赖管理工具。依赖管理工具在业界已经是无处不在,yum、maven、gradle、pip、npm、cargo这些工具的大名如雷贯耳。那么go有什么呢?

  早期go的依赖是混乱的,因为没有一个工具能得到普遍认可,而官方又迟迟不出来解决问题。历数存在的工具包括godep、glide、govender等等。甚至早期还需要使用GOPATH来管理依赖,即项目的所有依赖都通过go get下载到指定的GOPATH中去。当然这种方案还可以撑大多数时间,但随着时间的流逝,随着开发人员的变动,这种管理依赖的弊端就慢慢显现出来。其实这些老路早期的java也走过,曾几何时,每个java项目里面都会有一个叫lib或libs的目录,这里放的就是当前项目依赖的包。当GO采用GOPATH来管理依赖时,开发人员只能被倒逼着用java的方式在源码库中自行管理依赖。这样相当于给依赖包做了隔离,同时又具备了版本管理(因为放在源码库)。

  后来在go1.5的时候,官方引入了vender的概念,其实这也没改变多少,只是官方让大家存放依赖包的目录名称不要乱起了,统一叫vender吧。这个方案我觉得比依赖GOPATH还糟糕,因为vendor目录脱离了版本管理,导致更换依赖包版本很困难,在当前项目对依赖包的版本更新可能会影响其他项目的使用(如果新版本的依赖包有较大变动的话),同时如何将依赖包放到vendor下呢?等等。当然官方做出的这些变动可能是想像maven那样,推动社区来完成这件事,因而直接推动了上文提到的基于vendor的依赖管理工具的诞生。直至后来官方默认的社区做出来dep,这下安静了,尽管刚开始时也不怎么好用,但有总比没有好。

  go1.11在vgo的基础上,官方推出了go module。在发布前,官方与社区的大神们还为此开吵,认为官方太不厚道且独断专行。完全忽视dep社区的存在,无视dep在go语言中的地位与贡献。喜欢八卦的朋友们,可搜索《关于Go Module的争吵》一览大神是怎么吵架的,也可从中学习他们的思想。

  作为dep与module的亲身实践者,还是乖乖地用dep吧。除非你有足够的热情去折腾,比如弄个高速的科学上网工具,或者搭建Go module proxy。开源的go module proxy比如athens,goproxy。《Hello,Go module proxy》一文详细介绍了go module的幸福与烦恼,反正我是没有感到幸福。此文很全面地介绍go module后遗症,goproxy可以解决科学上网的问题,然后也有了athens这样的工具出现,go的依赖管理是朝着越来越好的方向发展。dep与module的争议在于respect and free,至少目前看来,两者是可以共存的,特别是在国内。

  相对于java的依赖管理工具maven或gradle来说,gradle是maven的升级版,同时带来了DSL与元编程的特性,这无疑使得gradle异常地强大。但gradle.io在国内的可达情况也不尽如人意,好就好在其与maven仓库标准的兼容,使得从maven转到gradle几乎没有额外的成本及阻力。

  扯了这么多,依赖管理对于一门语言是必不可少的。c有cmake,java有maven、gradle,rust有cargo,那么go的dep或者module就用起来吧,看完大神吵架之后,喜欢哪个就选哪个。是不可能产生一个能满足所有人要求的依赖管理工具的,就连号称最牛逼的cargo也不例外。在一般的项目中,能用到的依赖管理功能也就那常用的几个而已,对大多数项目来说,适用好用就行。

构建方式

  gopher第七条:按需构建

  构建的目标是让代码成为可运行程序。构建的过程应该是低成本并且让人愉悦的,显然C在这一方面让人抓狂,而go确实做得不错。并且能在任何平台下编译出另外一个平台的可执行程序。不管你的go程序是CLI、GUI、WEB或者其他形式的网络通讯程序,在go的世界里都只需要一个命令构建成可执行程序(依赖也一并被打包),即可在目标系统上运行。在这一点上,java是望尘莫及了。

下面是用来构建go程序常用的参数,其他参数可通过go help environment命令查看。

参数 说明
CGO_ENABLED 0 or 1 是否支持cgo命令,如果go代码中有c代码,需要设置为1
GOOS darwin, freebsd, linux, windows 可执行程序运行的目标操作系统
GOARCH 386, amd64, arm 可执行程序运行的目标操作系统架构
# Linux下编译Mac 64位可执行程序
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go # Linux下编译windows 64位可执行程序
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go # 如果想减少二进制程序的大小,可以加上-ldflags "-s -w",但同时会丢掉调试信息,即不能用gdb调试了。
# 如果想更进一步减少程序大小,可以使用加壳工具,比如upx

请关注公众号

不一样的go语言-gopher的更多相关文章

  1. 不一样的go语言-构建系统与构件系统

    前言   代码的最后一步是构建成计算机可识别的二进制数据,然后才得以在计算机上运行.如果你曾经写过有点规模(至少数十个以上独立的源文件,且需要依赖第三方包)C语言项目,必定对C语言项目的构建过程印象深 ...

  2. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

  3. C语言SOCKET编程指南

    1.介绍 Socket 编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措? ...

  4. Go语言学习资料汇总

    网站: Go语言官网(访问)(中文镜像) Go语言中文网(访问) Go编译器(访问) Go语言中国社区(访问) golanghome(访问) GoLang中国(访问) Gopher Academic( ...

  5. XXE篇-本着就了解安全本质的想法,尽可能的用通俗易懂的语言去解释安全漏洞问题

    0x01 Brief Description XXE(XML External Entity) XML外部实体攻击也是常见的web漏洞之一,在学习这个漏洞之前有必要了解一下xml,可以参考w3c的基本 ...

  6. C语言 HTTP上传文件-利用libcurl库上传文件

    原文  http://justwinit.cn/post/7626/ 通常情况下,一般很少使用C语言来直接上传文件,但是遇到使用C语言编程实现文件上传时,该怎么做呢? 借助开源的libcurl库,我们 ...

  7. Go语言Web框架gwk介绍2

    Go语言Web框架gwk介绍 (二) HttpResult 凡是实现了HttpResult接口的对象,都可以作为gwk返回Web客户端的内容.HttpResult接口定义非常简单,只有一个方法: ty ...

  8. Go从入门到精通(一)go语言初始

    一.第一个go程序 package main import ( "fmt" ) func main(){ fmt.Println("hello world") ...

  9. Go语言基础知识

    Go语言的一般结构:basic_structure.go Go程序是通过package来组织的,只能同过package名称为main的包可以包含main函数(一个可执行程序只能有一个main包) 通过 ...

随机推荐

  1. B - Fuzzy Search (FFT)

    题目链接:https://cn.vjudge.net/contest/281959#problem/B 题目大意:给你n,m,k.然后输入两个字符串,n代表第一个字符串s1,m代表第二个字符串s2,然 ...

  2. python - 类的内置 attr 方法

    类的内置 attr 方法 #类的内置 attr 方法: # __getattr__ # __setattr__ # __delattr__ # __getattr__ #到调用一个类不存在数参数时,将 ...

  3. CSS font-family 各名称一览表

    参考链接:https://blog.csdn.net/cddcj/article/details/70739481

  4. MongoDB的增删查改基本操作

    MongoDB的增删查改基本操作 先决条件建库.建集合.建文档 连接mongo,如果连接不上什么连接拒绝,输入mongod命令,启动服务后 输入mongo show dbs 显示当前的所有的数据库 一 ...

  5. Linux下svn常用指令【转】

    转自:http://blog.csdn.net/myarrow/article/details/8110858 Windows下的TortoiseSVN是资源管理器的一个插件,以覆盖图标表示文件状态, ...

  6. opencv学习笔记(九)Mat 访问图像像素的值

    对图像的像素进行访问,可以实现空间增强,反色,大部分图像特效系列都是基于像素操作的.图像容器Mat是一个矩阵的形式,一般情况下是二维的.单通道灰度图一般存放的是<uchar>类型,其数据存 ...

  7. 云服务器 linux文件系统异常an error occurren during the file system check导致服务器启动失败

    云服务器 linux文件系统异常an error occurren during the file system check导致服务器启动失败 文件系统宕机,重启后报错,无法启动 处理流程: 1.编辑 ...

  8. centos系统设置通过windows代理上网

    网络环境说明: 物理机windows xp sp3系统 ip:192.168.29.21(通过路由上网,有权限设置proxy给其他机器代理上网) 虚拟机centos5.5系统 ip:192.168.2 ...

  9. Mac配置Java开发环境

    笔者从Window上转到Mac上做开发,一切配置都要重新开始,开发环境配置介绍如下: 1. 下载JDK 从下面链接选择合适版本的安装包进行下载...笔者下载的是jdk-9.0.1 链接:http:// ...

  10. wpf 自定义属性的默认值

    public int MaxSelectCount { get { return (int)GetValue(MaxSelectCountProperty); } set { SetValue(Max ...