Go神分享最佳实践与设计模式

在日常Go编程工作中,这些才是真正的亮点:Go-local 模式与一些类似 GoF设计模式的结合,在一定程度上习以为常地融入了 Go:

1、使用函数值进行有状态处理,有时在struct体上使用方法表达式实现

2、Go 通过匿名函数、函数值和方法表达式为Gof命令模式 增色不少。

3、小型消费端接口文化让使用Gof策略模式 变得轻而易举。

4、sync.WaitGroup 和 channels 以及包 errgroup 支持多种连接模式,职责链模式

5、通道channels 非常适合通知模式(发布订阅),尤其是关闭一个 chan(struct{}) 值以供多个监听器使用的一次性通知。

6、结构化数据的匿名类型定义
例如:

  • 直接在函数内部声明类型 T struct { ...} 直接在函数内部声明,供本地使用,
  • 或使用 var 块来定义正确类型的数据 var v struct { ...}{ ...},这对于数据交换非常有效。在表格测试定义中,人们可能在不知不觉中使用了这种方法。

7、使用匿名块 { ...} :在函数中创建较小的作用域,以避免尴尬的标识符消歧。

8、使用指针进行值投射:
例如:使用 (*flag.FlagSet).BoolVar 将 *bool 投射到像 Config 类型的结构体:

struct { DryRun bool }
   with func (cfg *Config)

RegisterFlags(fs *flag.FlagSet) {
   fs.BoolVar(&cfg.DryRun, "dry-run", true, "是否更改数据库"
}

这对于避免库中的全局状态非常有效。

9、使用零值语义可实现安全、符合人体工程学的 API。
想象一下:

type ChangeBatch struct {
  Commit bool
 /* 其他字段省略 */
}

您可能希望 ChangeBatch 在默认情况下有效地像dry-run类型一样运行。

只要字段 Commit 为 false,ChangeBatch 就会以这种方式运行。

由于语言中的零值默认语义,Commit 总是 false,除非您将其明确设置为 true:
例如,

var b ChangeBatch
b.Commit ==true

10、可以在任何类型上定义方法--只要是需要的(没有理由限制方法只能用于结构类型)。除了满足特定领域的建模需求外,这还能很好地与语言中的各种模板引擎配合使用。

11、通过类似装饰器的方法,可以将常用的处理程序 API(如 http.Handler)串联起来,从而获得极佳的可编译性和重用性。

func trace(t Tracer, h http.Handler) http.Handler {
   return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { t := t.NewTrace(); defer t.End(); h(w, req) }.
}

11、使用包上下文package context 构建内部异步执行操作的同步 API。context.Context API 提供了协调、取消和清理此类操作的机制。

12、使用 (*testing.T).Helper 创建测试设置和拆卸助手。

13、使用在测试上下文中返回错误的函数,对类型的第三方实现执行可扩展的黑盒验证(extensible blackbox validations ),就像 fstest.TestFS 一样。这就是平衡可维护性的适度抽象。

很少使用的设计模式:

  • 工厂:很少有情况需要使用这种类型的反转,我传递一个值的唯一目的是创建其他值。
  • 观察者/回调:通常使用通道更为正确。如果我必须设计一个 API(已命名的函数类型或接口),并由他人提供植入,我就需要仔细记录所有与锁定、阻塞、错误、输入和输出等相关的不变式。这需要做大量的工作(这也适用于其他语言)。因此,我倾向于将这些内容留作最后的工具。
  • 访问者模式:情况同上。我通常会选择实现一个本地迭代器,比如 bufio.Scanner.View。
  • 构建器Builder模式:通常需要大量的模板来实现它们。投资回报率通常不高。

基本上不使用的设计模式:

  • 供应者和提供者(Suppliers and Providers):只有在处理那些抽象概念混乱的代码时才需要这种间接方式。
  • 懒惰初始化:99% 的情况下,这表明我不知道我的不变式,也无法对我的代码和数据流进行推理。

从不使用的设计模式:

  • 单例:在所有语言中,全局状态都不利于代码健康。
  • Flywheel模式:只在 Java 中用过一次。

(其中一些观点来自于我曾是许多团队使用的库和中间件的所有者。这导致我对可维护性持相当悲观的态度。如果只有我一个人使用代码,而其他人并不使用,我可能会稍微放宽一些观点)。