forked from XChainLab/documentation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path目录结构和编码规范
371 lines (274 loc) · 7.6 KB
/
目录结构和编码规范
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
目录结构规范
NAME
├── README.md 介绍软件及文档入口
├── bin 编译好的二进制文件,执行./build.sh自动生成,该目录也用于程序打包
├── build.sh 自动编译的脚本
├── doc 项目的文档
├── pack 打包后的程序放在此处
├── pack.sh 自动打包的脚本,生成类似xxxx.20170713_14:45:35.tar.gz的文件,放在pack文件下
└── src 项目的源代码
├── api 接入管理
├── node 节点管理
├── consensus 共识机制
├── core 核心功能
├── account 账户
├── asset 资产
├── blockchain 区块链
├── transaction 交易
├── validator 验证
├── witness 证明
└── state 状态
├── policy 政策合规
├── crypto 签名加密
├── config 配置管理
├── vm 虚拟机
├── smartcontract 智能合约
├── storage 存储
├── cache 内存缓存
├── database 数据持久化
├── sql
├── nosql
└── file
├── json json格式
└── wrapper 封装
├── network 对等网络
├── wallet 钱包接口
└── vendor 依赖库
├── github.com/xxx 第三方库
└── xxx.com/obc 公司内部的公共库
编码规范 (Golang)
文件名命名规范
用小写,尽量见名思义,看见文件名就可以知道这个文件下的大概内容,对于源代码里的文件,文件名要很好的代表了一个模块实现的功能。
命名规范
包名
包名用小写,使用短命名,尽量和标准库不要冲突
接口名
单个函数的接口名以”er”作为后缀,如Reader,Writer
接口的实现则去掉“er”
type Reader interface {
Read(p []byte) (n int, err error)
}
两个函数的接口名综合两个函数名
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
三个以上函数的接口名,类似于结构体名
type Car interface {
Start([]byte)
Stop() error
Recover()
}
变量
全局变量:采用驼峰命名法,仅限在包内的全局变量,包外引用需要写接口,提供调用 局部变量:驼峰式,小写字母开头
常量
常量:大写,采用下划线
import 规范
import在多行的情况下,goimports会自动帮你格式化,在一个文件里面引入了一个package,建议采用如下格式:
import (
"fmt"
)
如果你的包引入了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包:
import (
"encoding/json"
"strings"
"myproject/models"
"myproject/controller"
"git.obc.im/obc/utils"
"git.obc.im/dep/beego"
"git.obc.im/dep/mysql"
)
在项目中不要使用相对路径引入包:
// 这是不好的导入
import “../net”
// 这是正确的做法
import “xxxx.com/proj/net”
函数名
函数名采用驼峰命名法,尽量不要使用下划线
错误处理
error作为函数的值返回,必须尽快对error进行处理
采用独立的错误流进行处理
不要采用这种方式
if err != nil {
// error handling
} else {
// normal code
}
而要采用下面的方式
if err != nil {
// error handling
return // or continue, etc.
}
// normal code
如果返回值需要初始化,则采用下面的方式
x, err := f()
if err != nil {
// error handling
return
}
// use x
Panic
在逻辑处理中禁用panic
在main包中只有当实在不可运行的情况采用panic,例如文件无法打开,数据库无法连接导致程序无法 正常运行,但是对于其他的package对外的接口不能有panic,只能在包内采用。 建议在main包中使用log.Fatal来记录错误,这样就可以由log来结束程序。
Recover
recover用于捕获runtime的异常,禁止滥用recover,在开发测试阶段尽量不要用recover,recover一般放在你认为会有不可预期的异常的地方。
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
// do 函数可能会有不可预期的异常
do(work)
}
Defer
defer在函数return之前执行,对于一些资源的回收用defer是好的,但也禁止滥用defer,defer是需要消耗性能的,所以频繁调用的函数尽量不要使用defer。
// Contents returns the file's contents as a string.
func Contents(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close() // f.Close will run when we're finished.
var result []byte
buf := make([]byte, 100)
for {
n, err := f.Read(buf[0:])
result = append(result, buf[0:n]...) // append is discussed later.
if err != nil {
if err == io.EOF {
break
}
return "", err // f will be closed if we return here.
}
}
return string(result), nil // f will be closed if we return here.
}
控制结构
if
if接受初始化语句,约定如下方式建立局部变量
if err := file.Chmod(0664); err != nil {
return err
}
for
采用短声明建立局部变量
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
range
如果只需要第一项(key),就丢弃第二个:
for key := range m {
if key.expired() {
delete(m, key)
}
}
如果只需要第二项,则把第一项置为下划线
sum := 0
for _, value := range array {
sum += value
}
return
尽早return:一旦有错误发生,马上返回
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)
方法的接收器
名称 一般采用strcut的第一个字母且为小写,而不是this,me或者self
type T struct{}
func (p *T)Get(){}
如果接收者是map,slice或者chan,不要用指针传递
//Map
package main
import (
"fmt"
)
type mp map[string]string
func (m mp) Set(k, v string) {
m[k] = v
}
func main() {
m := make(mp)
m.Set("k", "v")
fmt.Println(m)
}
//Channel
package main
import (
"fmt"
)
type ch chan interface{}
func (c ch) Push(i interface{}) {
c <- i
}
func (c ch) Pop() interface{} {
return <-c
}
func main() {
c := make(ch, 1)
c.Push("i")
fmt.Println(c.Pop())
}
如果需要对slice进行修改,通过返回值的方式重新赋值
//Slice
package main
import (
"fmt"
)
type slice []byte
func main() {
s := make(slice, 0)
s = s.addOne(42)
fmt.Println(s)
}
func (s slice) addOne(b byte) []byte {
return append(s, b)
}
如果接收者是含有sync.Mutex或者类似同步字段的结构体,必须使用指针传递避免复制
package main
import (
"sync"
)
type T struct {
m sync.Mutex
}
func (t *T) lock() {
t.m.Lock()
}
/*
Wrong !!!
func (t T) lock() {
t.m.Lock()
}
*/
func main() {
t := new(T)
t.lock()
}
如果接收者是大的结构体或者数组,使用指针传递会更有效率。
package main
import (
"fmt"
)
type T struct {
data [1024]byte
}
func (t *T) Get() byte {
return t.data[0]
}
func main() {
t := new(T)
fmt.Println(t.Get())
}