查看原文
其他

Go语言常见错误| 滥用getters/setters

云原生Go 源自开发者
2024-08-28

在Go语言编程中,恰如其分地使用getters和setters是至关重要的。过度和不适当地使用它们可能导致代码冗余、可读性差和封装不当。在本文中,我们将深入探讨如何识别滥用getter和setter的情况,以及如何采取最佳实践来避免这些常见的Go错误。

理解Go中的封装

在开始之前,有必要了解Go语言中的封装原则。在许多面向对象的语言中,封装通常通过使用私有字段(private)和公共方法(public)(即getters和setters)来实现。然而,在Go中,并没有明确的“private”和“public”关键字,而是通过大写字母开头的标识符来表达公有(可导出的)成员,小写字母开头的标识符表示私有(非导出的)成员。

常见误区

误区一:为每个字段创建getter和setter

一个常见的错误是为结构体中的每个字段都创建getter和setter方法,即使它们不需要特殊逻辑来访问或修改。

例子:

type Person struct {
    name string
}

func (p *Person) GetName() string {
    return p.name
}

func (p *Person) SetName(name string) {
    p.name = name
}

如何避免:

在Go中,如果字段不需要特殊的访问控制,那么它们应该被直接暴露,而不是通过getter和setter。

改进后的例子:

type Person struct {
    Name string
}

误区二:getter和setter中的不必要逻辑

避免在getter和setter中增加不必要的逻辑,因为这些额外的步骤可能不是调用者预期的。

例子:

func (p *Person) GetName() string {
    return "Name: " + p.name // Unnecessary formatting
}

func (p *Person) SetName(name string) {
    p.name = strings.TrimSpace(name) // Unnecessary trimming
}

如何避免:

Getter应只返回值,setter应只设置值。如果需要对数据进行格式化或清理,请使用单独的方法来明确这些意图。

改进后的例子:

type Person struct {
    Name string
}

func (p *Person) SetName(name string) {
    p.Name = name
}

func (p *Person) FormattedName() string {
    return "Name: " + p.Name
}

func (p *Person) CleanName() {
    p.Name = strings.TrimSpace(p.Name)
}

误区三:setter方法返回值

在Go中,setter方法一般不应该有返回值,因为这可能会导致混乱。

例子:

func (p *Person) SetName(name string) bool {
    if name == "" {
        return false
    }
    p.name = name
    return true
}

如何避免:

将setter方法设计为void函数。如果需要错误处理,可以考虑返回error类型。

改进后的例子:

func (p *Person) SetName(name string) error {
    if name == "" {
        return errors.New("name cannot be empty")
    }
    p.Name = name
    return nil
}

误区四:违反Go语言的约定

在一些其他语言中,使用get和set前缀是普遍的命名约定。而在Go中,通常省略这些前缀,应遵循Go的简洁和直率的命名风格。

例子:

func (p *Person) GetAge() int {
    return p.age
}

func (p *Person) SetAge(age int) {
    p.age = age
}

如何避免:

简化方法名,避开get和set前缀,除非它们增加了方法的清晰性。

改进后的例子:

type Person struct {
    name string
    Age int
}

误区五:在简单转发的getter/setter中添加锁

在并发编程中,为了线程安全,可能会在getter和setter方法中添加锁。然而,这有时候其实是一个过度设计。

例子:

type ThreadSafePerson struct {
    name string
    mu sync.Mutex
}

func (p *ThreadSafePerson) GetName() string {
    p.mu.Lock()
    defer p.mu.Unlock()
    return p.name
}

func (p *ThreadSafePerson) SetName(name string) {
    p.mu.Lock()
    defer p.mu.Unlock()
    p.name = name
}

如何避免:

如果结构体是immutable的或者很少修改,那么简单的读写可能不需要锁。考虑结构体的使用场景,只在确实需要的时候加锁。

改进后的例子:

type ThreadSafePerson struct {
    Name string // Assume atomic or rarely changed fields
}

总结

在Go中避免滥用getters和setters需要对Go的封装原则有深入的理解。简化你的API,保持方法直白,遵循Go的命名规范,这样将让你的代码更加清晰和易于维护。


文章精选

Go语言常见错误| 意外的变量隐藏

Go语言常见错误| 不必要的代码嵌套

Go语言常见错误| 误用init函数

使用 Go 实现跨平台的蓝牙聊天室

Go 数据库操作全解析

深入理解 Go 中的 CGo 编程

Go 语言中使用切片而非数组的理由

高效团队的 Go 编码规范


点击关注并扫码添加进交流群领取「Go 语言」学习资料

继续滑动看下一个
源自开发者
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存