0%

学习uber-go开发规范笔记

文档: https://github.com/uber-go/guide

中文: https://github.com/xxjwxc/uber_go_guide_cn

在阅读中发现自己当前认知上的不足之处:

  1. 把接口Interface只当作值传递即可, 接口底层实际上就是两个指针type和data

  2. 尽可能在编译期让实现接口的错误被检查出来, 例如让通过var _ http.Handler = (*Handler) nil 来检查*Handler是否真的实现了http.Handler

  3. slice或map是指针, 在使用临界处需要进行拷贝传递以避免多方使用时修改到同一个底层数据

  4. 函数参数在定义时间的参数使用time.Durationtime.Time, 避免使用int来表示. 另外在json解析时最好加上int类型的单位标记,如IntervalSeconds int 'json:"intervalSeconds"' 而不是直接使用Interval

  5. 尽量返回error而不是panic, 只在调用方或main处理error.

  6. 对于需要进行原子操作的类型直接声明位atomic.XX类型, 避免使用int、int32等类型, 以减少忘记使用atomic包中方法来操作这个数据导致的非原子化问题.

  7. 避免使用可变全局变量, 而使用选择依赖注入的方式, 通过struct和new的方式生成唯一全局变量来操作

  8. 在结构体中避免使用嵌入式的类型, 内嵌的类型放在结构体顶部并空一行以隔开非嵌入类型, 而是使用委托方法来代理访问底层结构体的方法, 可以方便后续变更.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // bad
    // AbstractList 是各种实体列表的通用实现。
    type AbstractList interface {
    Add(Entity)
    Remove(Entity)
    }
    // ConcreteList 是一个实体列表。
    type ConcreteList struct {
    AbstractList
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // good
    // ConcreteList 是一个实体列表。
    type ConcreteList struct {
    list AbstractList
    }
    // 添加将实体添加到列表中。 自己代理封装了
    func (l *ConcreteList) Add(e Entity) {
    l.list.Add(e)
    }
    // 移除从列表中移除实体。
    func (l *ConcreteList) Remove(e Entity) {
    l.list.Remove(e)
    }
  9. 尽可能初始化指定map/slice容量, strconv性能比fmt好, 尽可能一次性完成string->[]byte的操作, 这个转换操作是有开销代价的.

  10. 变量声明分组, 相似的分一组. 导入也分组, 标准库/第三方库/私有库分组, 一个文件中, 结构体-new-方法形式分成一组, 其他工具函数放在结尾

  11. 全局的未导出的变量使用_前缀以表明身份, 方便区分以及减少被局部同名变量覆盖的可能.

  12. nil可以是一个有效的slice, 可以通过return来返回, 避免返回空slice如:[]int{}, 对slice的判空使用len(), 声明用var优于[]int{}

  13. 反引号可用于避免字符串转义, 不用手动加转义符

  14. map初始化有初始值用map[T1]T2{xx, xx..}形式, 否则用make方式, 结构体用=&=初始化