【问题标题】:Encapsulating struct in Go在 Go 中封装结构
【发布时间】:2018-03-06 08:54:15
【问题描述】:

我是 Go 新手。我读过 Go 中的封装是在包级别上的。我有一个简单的网络控制器用例。我有一个 struct,它以 JSON 对象的形式出现,并被解组到结构类型中。

type User struct{
    Name String `json:"name"`
    //Other Variables
}

现在可以通过 json.Unmarshal([]byte) 将 json 解组为 User Struct 类型。但是,此 User 结构也可用于其他包。如何确保只有与 User 相关的方法才能被其他包访问。

我能想到的一个解决方案:

type User struct{
    name String
}

type UserJSON struct{
    Name String `json:"name"`
}

func DecodeJSONToUser(rawJSON []byte) (User,error) {
    var userJSON UserJSON
    err := json.Unmarshal(rawJSON,&userJSON)
    //Do error handling
    return User{name:userJSON.Name},nil
}

有没有一种可行的方法来实现这一点?

【问题讨论】:

  • 是的,有一种方法是你不想被其他包使用的结构字段不导出它们。
  • @GrzegorzŻur 我希望其他包可以在 User Struct 上工作,例如调用 User.GetName() 而不是直接调用 User.Name。
  • @Himanshu 是的,但是想想这个用例,其中 struct User 的每个成员都来自 JSON,因此 json.Unmarshal 将要求导出所有 struct 字段。现在,谁来阻止像 User.Name = "some_other_name" 之类的东西?
  • 创建一个包在解组数据时分配结构,并且不要在主目录中导入该包,这样除了包之外没有其他用户可以修改结构。将你的结构封装在一个包中

标签: go struct


【解决方案1】:

您可以将 package local structpublic 字段一起使用,这样该结构在包外将不可见。然后你可以让这个结构满足一些公共接口,你就有了完美的解耦:

package user

import "encoding/json"

type User interface {
    Name() string
}

type user struct {
    Username string `json:"name"`
}

func (u *user) Name() string {
    return "Mr. " + u.Username
}

func ParseUserData(data []byte) (User, error) {
    user := &user{}
    if err := json.Unmarshal(data, user); err != nil {
        return nil, err
    }
    return user, nil
}

以及相应的测试:

package user_test

import (
    "testing"

    "github.com/teris-io/user"
)

func TestParseUserData(t *testing.T) {
    data := []byte("{\"name\": \"Uncle Sam\"}")
    expected := "Mr. Uncle Sam"
    if usr, err := user.ParseUserData(data); err != nil {
        t.Fatal(err.Error())
    } else if usr.Name() != expected {
        t.Fatalf("expected %s, found %s", expected, usr.Name())
    }
}

➜ 用户 git:(master) ✗ 去测试 github.com/teris-io/user

好的 github.com/teris-io/user 0.001s

您还可以在解组后将您的包本地对象转换为一些公共对象。

注意:其中一个 cmets 提到,由于名称冲突(结构上的字段 user.Name,接口上的方法 User.Name),接口需要有一个不同的方法名称。这不是必须的,上面的代码已经做了相应的修改:内部结构上的字段可以有一个与JSON中的不同的名称,对应的注解定义了映射。

【讨论】:

  • 您为什么不建议编辑@Thomas 的答案?两种解决方案的区别非常细微。
  • 细微的差异有时会产生至关重要的影响
  • 喜欢这个!让我有点困扰的一件事是,getter GetName() 应该被称为 Name()。但在这种情况下,您不能因为标签的原因在 user 结构中需要一个公共字段。
  • @MilanVlach 这并没有那么戏剧化,更改了代码以适应这个要求(见注释)
猜你喜欢
  • 2011-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-25
  • 1970-01-01
相关资源
最近更新 更多