发布于 

Golang 开发“疑难杂症”解决办法 🤔⁉️

Go 开发中容易出现的错误案例分析与解决办法

工程架构不合理

  • 混乱的代码作用域导致 变量隐藏
    • 代码通过编译,但数据并没有赋值到预期的变量
    • 采用局部临时变量(:=),再赋值给目标变量
    • 直接采用赋值操作符赋值给目标变量(可读性和性能较好,还能抽离统一处理 err
    • Go 变量:内存中可寻址、寻址是有范围的
  • 不必要的 嵌套 导致代码可读性差
    • 软件的思维模型是对系统行为的内部表达方式
    • 影响可读性的一个关键方面:嵌套层的数量
    • 把正确的代码路径向左对齐,以方便查看预期的执行流
    • 异常情况通过 if 表示,去 else ,减少代码块
  • 滥用 init 函数 使代码流难以管理
    • 需要进行错误管理、涉及状态的逻辑不依赖 init 函数
    • init 函数不便进行单元测试
  • 过度使用 getter 和 setter 导致 代码淹没风险
    • getter 和 setter 的优点:封装、隐藏、调试
    • 不需要就不用这种方式,保持语言简洁特性
  • 使用不必要的抽象导致 接口污染
    • 接口越大,抽象越弱。— Rob Pike
    • 何时使用接口:
      • 常见的行为,如 sort.Interface
      • 解耦(设计模式,组件可替换)
      • 限制行为(语义上通过接口类型来限制特定行为)
    • 发现抽象,而不是创建抽象(使用接口,而不是随意创建接口)
    • 不要用接口进行设计,要发现他们。— Rob Pike

项目难维护难扩展

  • 不要 过度抽象,要根据客户端(功能调用方)的实际需求
  • 做什么要保守,接收什么要自由
    • 尽可能不要返回接口,而是返回具体的实现;接收接口
    • 也有例外,比如返回 error 接口
  • any 代表着 nothing,避免 过度泛化 错误来规避
    • any (interface{}) 不能传达任何信息
    • encoding/jsonMashal 形参是 any
    • database/sqlQueryContext 查询 query 是 any
  • 使用 泛型,避免运行时开销与代码冗余,泛型用法:
    • 数据结构、提取行为、处理任何类型的 slice、map 和 channel 的函数
    • 当调用类型参数的方法时、当泛型使代码更加复杂时不该使用泛型
  • 结构体字段 类型嵌入、接口与嵌入
    • 避免方法被提升导出(不额外命名私有变量,而是直接嵌入)
    • 嵌入对比 OOP 中的子类化(组合和继承的区别)
    • 类型嵌入约束:他不是语法糖、不应该提升本应该隐藏的元素

设计模式不清晰

数据类型内存碎片化

CPU 缓存命中错误

运行时紊乱和 GC 超负 荷

指令并行错误

内存堆积和 GC 无效

栈空间逃逸和堆碎片化

GC 三色标记算法效率

大对象生命周期管理

并发数据竞争

优化错误管理

低效 I/O 多路复用

CPU 缓存行伪共享

调度器负载过重

频繁触发 CG 写屏障

CG 写屏障卡表更新错误

CPU 多级告诉缓存未命中

总线监视异常

测试颗粒度混乱

测试不充分/逻辑过度复杂

CPU 高速缓存一致性错误

缓存污染和数据竞争

代码优化的复杂问题

参考资料