Go是一门推崇软件工程理念的编程语言,它为开发周期的每个环节都提供了完备的工具和支持。

Go语言高度强调代码和项目的规范和统一,这几种体现在工程结构或者说代码体制的细节之处。

1.工作区

一般情况下,Go源码文件必须放在工作区中。但是对于命令源码文件来说,这不是必须的。
工作区其实就是一个对应于特定工程的目录,它应该包含3个子目录:src目录、pkg目录和bin目录。

(1)src目录

用于以代码包的形式组织并保存Go源码文件,这里的代码包与src下的子目录一一对应。
例如,若一个源码文件被声明属于代码包log,那么它就应该保存在src/log目录下。
当然,你也可以直接把Go源码文件直接放在src目录下,但这样的Go源码文件就只能被声明属于main代码包。
除非用于临时测试或演示,一般还是建议把Go源码文件放入特定的代码包中。

(2)pkg目录

用于存放通过go install命令安装后的代码包的归档文件。
前提是代码包中必须包含Go库源码文件。归档文件是指那些名称以“.a”结尾的文件。
该目录与GOROOT目录下的pkg目录功能类似。
区别在于,工作区中的pkg目录专门用来存放用户代码的归档文件。
编译和安装用户代码的过程一般会以代码包为单位进行。
比如log包被编译安装后,将生成一个名为log.a的归档文件,并存放在当前工作区的pkg目录下的平台相关目录中。

(3)bin目录

与pkg目录类似,在通过go install命令完成安装后,保存由Go命令源码文件生成的可执行文件。
(其实上面文字的意思就是什么类型的文件放在什么目录下面,尽量规划好工作区)

命令源码文件:就是声明属于main代码包并包含无参声明和结果声明的main函数的源码文件。
这类源码文件就是程序的入口,它们可以独立运行(使用go run命令),
也可以通过go build或go install命令得到相应的可执行文件。

库源码文件:是指存在于某个代码包中普通源码文件。

2.源码文件

(1)命令源码文件

如果一个源码文件被声明属于main代码包,且该文件代码中包含无参数声明和结果声明的main函数,则它就是命令源码文件。
命令源码文件可以直接通过go run命令直接启动运行。

同一个代码中的所有的源码文件,其所属代码包的名称必须一致。
如果命令源码文件和库源码文件处于同一个代码包中,那么在该包中就无法正确执行go build和go install命令。
换句话说,这些源码文件将无法通过常规方法编译和安装。
因此,命令源码文件通常会单独放在一个代码包中,因为通常一个程序模块或软件的启动入口只有一个。

同一个代码包中可以有多个命令源码文件,可通过go run命令分别运行,但这会使go build和go install命令无法编译和安装该代码包。

当代码包中有且只有一个命令源码文件时,在文件所在目录中执行go build命令,
则可在该目录下生成一个与目录同名的可执行文件;

若使用go install命令,则可在当前工作区的bin目录下生成相应的可执行文件。

需要注意的是,只有当前环境变量GOPATH中只包含一个工作区的目录路径时,
go install命令才会把命令源码文件安装到当前工作区的bin目录下;

(2)库源码文件

通常,库源码文件声明的包名会与它直接所属的代码包一致,且库源码文件中不包含无参数声明和无结果声明的main函数。

通过执行go install命令,成功安装了该包并生成了若干归档文件。

由于我们指定了工作区GOPATH的路径是/root/example.v2,则会在这个目录下生成bin和pkg文件。
run install之前:

run install之后:

(3)测试源码文件

测试源码文件是一种特殊的库文件,可以通过执行go test命令运行当前代码包下的所有测试源码文件。
成为测试源码文件的充分条件有两个:
  A.文件名需要以"_test.go"结尾
  B.文件中需要至少包含一个名称以Test开头或Benchmark开头,且拥有一个类型为*testing.T或*testing.B的参数的函数。
  *testing.T或*testing.B是两个结构体类型。而*testing.T或*testing.B则分别为前两者的指针类型。它们分别是功能测试和基准测试所需。

当在一个代码包中之心go test命令时,该代码包中的所有测试源码文件会被找到并运行。

3.代码包

在Go中,代码包是代码编译和安装的基本单元,也是非常直观的代码组织形式。

(1)包声明

在Go语言中,代码包中的源码文件可以任意命令。
但是,这些任意名称的源码文件都必须以包声明语句作为文件代码中的第一行。
例如,example.v2/src/gopcp.v2/helper/log/base包中的所有源码文件都先声明自己属于某一个代码包:

分别打印第一行:

其中,package是Go中用于包声明语句的关键字。
Go规定包声明中的包名是代码路径的最后一个元素。

所以包名是base。
但是,不论命令源文件存放在哪个代码包中,它都必须声明属于main包。

(2)包导入

代码包gopcp.v2/helper/log中的logger.go需要依赖base子包和logrus子包,

因此需要在源码文件中使用代码包导入语句,如:

import "gopcp.v2/helper/log/base"
import "gopcp.v2/helper/log/logrus"

全路径是:/root/example.v2/src/gopcp.v2/helper/log,但是所有的源代码都在src下面,所以这里使用相对路径。

当导入多个代码包时,你需要用圆括号,每个代码包名占一行。

import (
"gopcp.v2/helper/log/base"
"gopcp.v2/helper/log/logrus"
)

同一个源码文件中导入多个代码包的最后一个元素不能重复,否则一旦使用其中的程序实体,就会引起编译错误。
但是,如果你只导入不适用,同样会引起编译错误,一个解决办法就是为其中一个起别名。

import (
"gopcp.v2/helper/log/logrus"
mylogrus "gopcp.v2/helper/log/logrus"
)

如果我们想不加前缀而直接使用某个依赖包中的程序实体,就可以用"."来代替别名。

import (
. "gopcp.v2/helper/log/logrus"
)

所以我们就可以这样引用:

var logger = NewLogger("gopcp") //NewLogger是logrus中的一个函数。

我们可以看到,因为"."的缘故,可以直接引用包中的函数,而不需要指定路径。

在Go中,变量、常量、函数和类型声明可统称为程序实体,而它们的名称统称为标识符。
标识符可以是Unicode字符集中任意能表示自然语言文字的字符、数字以及下划线(_)。标识符不能以数字或下划线开头。

实际上,标识符的首字符的大小写控制着对应程序实体的访问权限。
如果标识符的首字符是大写形式,那么它所对应的程序实体就可以被本代码之外的代码访问到,也称之为可导出或公开的;
否则,对应的程序实体就只能被本包内的代码访问,也成为不可导出的或包私有的。
要想成为可导出的程序实体,还需要额外满足以下两个条件。
程序实体必须是非局部的。局部的程序实体是指:它被定义在了函数或结构体的内部。
代码包所属目录必须包含在GOPATH中定义的工作区目录中。

代码包导入还有另一种情况:如果只想初始化某个代码包,
而不需要在当前源码文件中使用那个代码包中的任何程序实体,就可以用“_”来代替别名。

import (
_ "gopcp.v2/helper/log/logrus"
)

这种情况下,我们只是触发了这个代码包的初始化操作,符号"_"就像一个垃圾桶。

(3)包初始化

在Go中,可以有专门的函数负责代码包初始化,成为代码包初始化函数。
这个函数无任何参数声明和结果声明,且名称必须为init。

func init() {
fmt.PrintLn("Initialize...")
}

Go会在程序真正执行前对整个程序的依赖进行分析,并初始化相关的代码包。
也就是说,所有的代码包初始化函数都会在main函数(命令源码文件的入口函数)执行前执行完毕,而且只会执行一次。
另外,对于每一个代码包来说,其中的所有全局变量的初始化,都会在代码包的初始化函数执行前完成。
这避免了在代码包初始化函数对某个变量进行赋值之后,又被该变量声明中赋予的值覆盖掉的问题
下面是一个简单示例:

//如果一个源码文件被声明属于main代码包,且该文件代码包含无参数声明和结果声明的main函数,则它就是命令源码文件。
package main //命令源码文件必须在这里声明自己属于main包
import ( //导入标准库代码包fmt和runtime
"fmt"
"runtime"
)
func init() { //代码初始化函数
fmt.Printf("Map: %v\n", m) //这里可以直接获取到变量m,可以看出变量的初始化在代码包初始化之前完成
//通过runtime获取当前操作系统和计算架构
//通过fmt的Sprintf进行格式化然后赋值给info
info = fmt.Sprintf("OS:%s,Arch:%s", runtime.GOOS, runtime.GOARCH)
} //非局部变量,map类型,且已初始化,因为已经赋值
var m = map[int]string{1: "A", 2: "B", 3: "C"}
//非局部变量,string类型,未初始化,因为还未赋值
var info string func main() { //命令源码文件必须有入口函数,也可以叫做主函数
fmt.Println(info) //代码包的初始化会在main函数执行前执行完毕
} //同一个代码包中可以存在多个代码初始化函数,甚至代码包内的每一个源码文件都可以定义多个代码初始化函数。
//Go不会保证同一个代码包中多个代码包初始化函数的执行顺序。
//被导入的代码吧的初始化函数总是会先执行,例如上面fmt和runtime中如果有init函数,那么就会先执行。

  

go——工程结构的更多相关文章

  1. Eclipse点击工程结构里任意文件或文件夹变拖动(或复制)的bug

    本文为原创文章,欢迎转载,但请注明出处http://www.cnblogs.com/yexiubiao/p/5204601.html,未在文章页面明显位置给出原文连接的,将保留追究法律责任的权利. 在 ...

  2. Android开发App工程结构搭建

    本文算是一篇漫谈,谈一谈关于android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角 ...

  3. 09-Java 工程结构管理

    (一)Java 工程结构管理 1.什么是Build Path: -- 一般包括:JRE运行时库 第三方功能扩展库(*.jar 格式文件) 其他的工程 其他的源代码或Class 文件 为什么使用~ :通 ...

  4. 构建iOS稳定应用架构时方案选择的思考,主要涉及工程结构,数据流思想和代码规范

    工程结构架构,减少耦合混乱以及防治需求大改造成结构重构,如何构建稳定可扩展可变换的工程结构的思考 我打算采用Information flow的方式自上而下,两大层分为基础层和展现层的结构.基础层分为多 ...

  5. iOS工程结构

          好的架构不是设计出来的,而是进化而来的! 写在前面 从2011年底开始学习iOS开发,到现在也已经快3年了,虽然中途没有一直进行iOS的开发(总是在Android和iOS间切换),但始终没 ...

  6. 读阿里巴巴Java开发手册v1.2.0之工程结构有感【架构篇】

    首先,把昨天那俩条sql语句的优化原因给大家补充一下,第一条效率极低,第二条优化后的,sql语句截图如下: 经过几个高手的评论和个人的分析: 第一条sql语句查询很慢是因为它首先使用了in关键字查询, ...

  7. Dubbo工程结构和Maven打包

    Dubbo 工程结构 待续 Dubbo pom版本设置 在升级Dubbo的pom版本时需要对每一个工程中子工程pom,子子工程pom进行修改,这是一个复杂繁琐的工作.为此有人手动一个个修改,好一点的用 ...

  8. 【源码】otter工程结构

    最近在搞数据同步相关的内容,需要对otter的代码进行扩展,所以需要先熟悉一下otter的源码.首先我们整体来看下otter的工程结构.otter的工程结构比较复杂,需要花费一定的时间来理解各个部分的 ...

  9. Spring Boot 2.x基础教程:工程结构推荐

    Spring Boot框架本身并没有对工程结构有特别的要求,但是按照最佳实践的工程结构可以帮助我们减少可能会遇见的坑,尤其是Spring包扫描机制的存在,如果您使用最佳实践的工程结构,可以免去不少特殊 ...

  10. goLand工程结构管理

    goLand工程结构管理  转 https://www.jianshu.com/p/eb7b1fd7179e 开始之前请确保安装好了 go语言环境并配置好了gopath环境变量 1.创建一个新目录并打 ...

随机推荐

  1. Mybatis热加载Mapper.xml

    开发的时候,写Mybatis Mapper.xml文件的时候,每次修改SQL都需要重启服务,感觉十分麻烦,于是尝试写了一个Mybatis的Mapper.xml热加载. 能在修改Mapper.xml之后 ...

  2. Android Studio无法启动,gradle下载不了 提示“building “ 项目名”gradle project info”

    Google在2013年I/O大会上发布了Android Studio,AndroidStudio是一个基于IntelliJ思想的新的Android开发工具.下面介绍一下Android Studio安 ...

  3. Oracle Tuning 总括

    oracle tuning 分为3个阶段 1. application 调优阶段, 包括设计的调优, SQL语句调优, 管理权限等内容, (这部分是我的重点) (调优人员 application de ...

  4. net mvc 小目标

    1.前台视图去找指定的控制器(非默认) 2.控制器去找指定的视图(非默认)

  5. linux 知识点拾遗

    文件名称 在 Linux 底下,每个档案或文件夹的文件名称最长能够到达 255 的字符,加上完整路径时,最长可达 4096 个字符; 因为 Linux 在文字接口下的一些指令操作关系,一般来说,您在设 ...

  6. 第0步:OracleRAC软件准备

    表1   软件准备列表 安装包属性 文件信息 Oracle 11.2.0.4 p13390677_112040_Linux-x86-64_1of7.zip   p13390677_112040_Lin ...

  7. structure machine learning projects 课程笔记

    orthogonalization/ one metric train.dev/test 划分 开发集和测试集一定来自同一分布  onthe  same distribution Human leve ...

  8. Lumen rule

    之前写了了laravel表单验证的生命周期:https://www.cnblogs.com/cxscode/p/7561277.html 今天来总结一下lumen的Validator的一些使用心得 可 ...

  9. Innodb间隙锁,细节讲解(转)

    关于innodb间隙锁,网上有很多资料,在此不做赘述,我们讲解一下关于innodb的间隙锁什么情况下会产生的问题. 网上有些资料说innodb的间隙锁是为了防止幻读,这个论点真的是误人子弟.了解inn ...

  10. iOS 状态栏更改为白色

    如果觉得在iOS 7启动期间状态栏黑色不合你意,以下方法可改变Status bar style成白色 在工程的plist添加 Status bar style,改变style值 默认是Gray sty ...