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. Oracle(2)数据库

    1.使用"||"连接多个字段,合并成一列 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFudGluZ21laQ==/font/5a ...

  2. 爬虫 (6)- Scrapy 实战案例 - 爬取不锈钢的相关钢卷信息

    超详细创建流程及思路 一. 新建项目 1.创建文件夹,然后在对应文件夹创建一个新的python项目 2.点击Terminal命令行窗口,运行下面的命令创建scrapy项目 scrapy startpr ...

  3. expr判断整数是相加的值,返回命令的返回值$? 是0,但是少数情况是1,例如1 + -1 ,$? 的结果是1 ,判断要大于1最准确

    [root@m01 ~]# expr 1 + 12[root@m01 ~]# echo $?0[root@m01 ~]# echo 1 - 51 - 5[root@m01 ~]# expr 1 - 5 ...

  4. 配置Docker中国区官方镜像http://get.daocloud.io/ 很好的一个源http://get.daocloud.io/#install-docker

    https://www.daocloud.io/mirror#accelerator-doc 配置Docker中国区官方镜像http://get.daocloud.io/ 很好的一个源http://g ...

  5. 小程序组件与api

    通过组合基础组件进行快速开发. 组件是视图层的基本组成单元. 所有组件都有的属性: 属性名 描述 注解 id 组件的唯一标示 保持整个页面唯一 class 组件的样式类 在对应的 WXSS 中定义的样 ...

  6. javaweb项目配jdk

    1.首先在你的本地安装jdk1.6,并将之添加至myeclipse中:window--preferences--installed   jres,add添加,格式D:\program files\jd ...

  7. Tomcat nginx log日志按天分割切割

    利用 Linux 自带的 logrotate 工具来实现按天切割日志.下方已 centos 7 系统为例来实践讲解. 原理 Logrotate是基于CRON来运行的,其脚本是/etc/cron.dai ...

  8. 关于Jquery Ajax的用法

    今天简单描述一下Jquery Ajax的用法,和我在使用过程中的一些看法,仅供自己娱乐和大家参考值之用! Jquery Ajax的重要性不言而喻,只从Jquery面世之后,终于解救了像我这种既做前台又 ...

  9. Cocos2d-x 3.0final 终结者系列教程08-画图节点Node中的锚点和坐标系

    图片问答,(仅仅要回答正确,锚点和坐标系就学会了) 1.下图一共同拥有几个填充为淡黄色的实心矩形? 选择:A,2个  B, 4个 C,1个 D,16个 答案.B.4个 2.下图的4个实心矩形排列在几行 ...

  10. Jmeter_远程启动 I

    Jmeter 是Java 应用,对于CPU和内存的消耗比较大,因此,当需要模拟数以千计的并发用户时,使用单台机器模拟所有的并发用户就有些力不从心,甚至会引起JAVA内存溢出错误. 其实,Jmeter的 ...