Golang中真的没有办法实现"Protected"可见性吗?

Golang中真的没有办法实现"Protected"可见性吗? 大家好,

我是一名经验丰富的程序员,但Go语言对我来说是新的。作为一个用Java写过大量代码的人,我有着根深蒂固的面向对象思维。我认为Go语言帮助我以新的方式编码,并且很多时候,我发现自己写出了更直接的代码,这出于某些原因是好的。然而,我确实非常怀念Java中那种“protected”(受保护)的可见性。

当我创建一个新的Go模块时,我发现自己被迫将类型和函数设为公开的(首字母大写),以便更好地组织代码,并在模块内的不同包之间使用这些函数。然而,我并不想让模块的用户看到它们。

举个例子,如果我的模块需要处理令牌,我希望它有一个像 StoreToken(token string) 这样的公开函数。在模块内部的另一个包中,我可能想要一个函数 WriteTokenToDB(token string),但我不希望这个函数对模块用户可见,因为它是模块内部架构的一部分,我不希望外部用户使用它,这毫无意义!

一种解决方法是把所有东西都放在同一个包里,但那样代码就会变得一团糟……

有什么建议吗?


更多关于Golang中真的没有办法实现"Protected"可见性吗?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

是的,我明白了。我正在努力摆脱Java的思维定式。

更多关于Golang中真的没有办法实现"Protected"可见性吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


哇,原来是这样!这看起来正是我一直在找的东西。我一定会仔细研究一下的!

不……我刚刚修改了整个项目的包声明,但由于我使用了包含代码文件的嵌套文件夹,最终遇到了完全相同的问题……

刚刚发现,我们在文件头部声明的包名在整个项目中可以保持一致,不受嵌套文件夹的影响。这或许能解决我的问题……

我注意到从Java转过来的人常犯的一个问题是过度创建包。在Go中,包通常倾向于宽泛,而不是像Java那样狭窄。

虽然我从未遇到过这个问题,但如果你确实需要这个功能,也许可以引入另一个包,并将你想要导出的所有函数都包装起来。只需在代码的其他部分使用这个导出包即可。

一种解决方式是将所有内容放在同一个包中,但那样代码会变得一团糟……

为什么只有两个与令牌相关的函数,代码就会变得一团糟呢?无论如何,我认为你正在寻找的是内部包:

使用内部包来减少你的公共 API 表面积 | Dave Cheney

你可以创建可复用的包,并导出那些可以被你的包导入但其他地方无法导入的函数。

在Go语言中,确实没有直接的“protected”可见性修饰符,但可以通过以下几种方式实现类似的访问控制效果:

1. 使用internal目录

这是Go官方推荐的方式。在模块根目录下创建internal/目录,其中的代码只能被同一模块内的其他包导入:

// 模块结构:
// mymodule/
// ├── go.mod
// ├── internal/
// │   └── tokenstore/
// │       └── store.go
// └── api/
//     └── client.go

// internal/tokenstore/store.go
package tokenstore

// 这个函数只能在mymodule内部使用
func WriteTokenToDB(token string) {
    // 实现细节
}

// api/client.go
package api

import "mymodule/internal/tokenstore"

func StoreToken(token string) {
    tokenstore.WriteTokenToDB(token)
}

2. 使用接口隐藏实现

通过接口暴露有限的功能,同时保持内部实现私有:

// token/token.go
package token

type TokenManager interface {
    StoreToken(token string)
}

type tokenManager struct {
    // 私有字段
}

func (tm *tokenManager) StoreToken(token string) {
    tm.writeTokenToDB(token)
}

// 私有方法,外部无法访问
func (tm *tokenManager) writeTokenToDB(token string) {
    // 实现细节
}

// 工厂函数返回接口类型
func NewTokenManager() TokenManager {
    return &tokenManager{}
}

3. 使用包级私有变量和函数

通过小写字母开头保持私有,在同一包内共享:

// token/internal.go
package token

// 包内私有函数
func writeTokenToDB(token string) {
    // 实现细节
}

// 公开函数
func StoreToken(token string) {
    writeTokenToDB(token)
}

4. 结合使用internal和接口

这是最接近“protected”可见性的方案:

// 模块结构:
// mymodule/
// ├── internal/
// │   └── impl/
// │       └── token_impl.go
// └── token/
//     └── token.go

// token/token.go
package token

type TokenStore interface {
    Store(token string)
    // 只暴露必要的方法
}

// token/internal/impl/token_impl.go
package impl

import "mymodule/token"

type tokenStoreImpl struct {
    // 私有字段
}

func (ts *tokenStoreImpl) Store(token string) {
    ts.writeToDB(token)
}

func (ts *tokenStoreImpl) writeToDB(token string) {
    // 内部实现,外部无法访问
}

// 内部使用的工厂函数
func NewTokenStore() token.TokenStore {
    return &tokenStoreImpl{}
}

5. 使用构建约束

通过构建标签控制代码的可见性:

// +build internal

package mymodule

// 这个函数只在编译时指定-tags=internal时才可用
func internalFunction() {
    // 内部实现
}

最推荐的方式是使用internal/目录,这是Go语言官方设计的用于模块内部代码组织的机制。虽然Go没有Java那样的protected关键字,但通过合理的包组织和接口设计,完全可以实现类似的访问控制效果。

回到顶部