Go 100 常见错误 4: 过度使用 getter 和 setter

36 阅读2分钟

在编程中,数据封装是指隐藏对象的值或状态。通过在未导出的对象字段上提供导出的方法,getter 和 setter 是实现封装的手段。

在 Go 语言中,并没有像一些其他语言那样自动支持 getter 和 setter。同样,使用 getter 和 setter 来访问结构体字段也被认为是不必要的,也不是一种惯用法。例如,标准库中实现了一些可以直接访问某些字段的结构体,如 time.Timer 结构体:

timer := time.NewTimer(time.Second)
<-timer.C // C 是一个 <--chan Time 类型的字段

尽管不推荐这样做,我们甚至可以直接修改 C(但我们将不再接收事件)。然而,这个例子说明了标准 Go 库甚至在不应该修改字段时,也不强制使用 getter 和/或 setter。

另一方面,使用 getter 和 setter 也有一些优点,包括以下几点:

  • 它们封装了与获取或设置字段相关的行为,允许以后添加新功能(例如,验证字段,返回计算值,或将字段访问包装在互斥锁周围)。
  • 它们隐藏了内部表示,给我们提供了更多的灵活性来决定我们暴露什么。
  • 它们在运行时属性更改时提供了调试拦截点,使调试变得更容易。

如果我们遇到这些情况,或者在确保向前兼容的同时预见到可能的用例,使用 getter 和 setter 可以带来一些价值。例如,如果我们在名为 balance 的字段上使用它们,我们应该遵循以下命名约定:

  • getter 方法应该命名为 Balance(而不是 GetBalance)。
  • setter 方法应该命名为 SetBalance。

以下是一个示例:

currentBalance := customer.Balance() // Getter
if currentBalance < 0 {
    customer.SetBalance(0) // Setter
}

总之,如果 getter 和 setter 在结构体上没有带来任何价值,我们不应该过度使用它们。我们应该务实地寻找效率和遵循有时在其他编程范式中被认为不容置疑的惯用法之间的正确平衡。

Go 是一种独特的编程语言,它为许多特性而设计,包括简单性。然而,如果我们发现需要使用 getter 和 setter,或者如前所述,在确保向前兼容的同时预见到未来的需要,使用它们并没有任何问题。