从父方法中访问所有字段

huangapple go评论88阅读模式
英文:

Access all fields from parent method

问题

我正在开发一个应用程序,其中数据存储在MongoDB中。有几个集合,当然它们都有一些共同的字段(如Id、创建日期等)和方法(例如插入)。在我的设想中,我需要创建一个带有所需字段和方法的基本模型结构,然后将这个结构嵌入到我的模型中。不幸的是,这样做不起作用,因为为基本模型定义的方法无法看到子字段。

我不知道如何进一步解释。这是在playground中的代码:
https://play.golang.org/p/_x-B78g4TV

它使用的是json而不是mgo,但思想仍然是一样的。

我希望输出结果是:

保存到'my_model_collection'

{"_id":42, "foo": "Some value for foo", "bar": "Here we set some value for bar"}

而不是:

保存到'my_model_collection'

{"_id":42}

为每个模型编写插入方法似乎违背了DRY原则,那么在Go中实现这一点的正确/惯用方法是什么?

英文:

I'm developing an application where data is stored in mongodb. There are several collections and of course all of them have some common fields (like Id, creation date, etc) and methods (for example Insert). In my vision, I need to create base model struct with needed fields and methods, and then embed this struct into my models. Unfortunately, this doesn't work because method defined for base model doesn't see child fields.

I don't know how to explain further. Here is code in playground:
https://play.golang.org/p/_x-B78g4TV

It uses json instead of mgo, but idea is still the same.

I want the output to be:
> Saving to 'my_model_collection'
>
> {"_id":42, "foo": "Some value for foo", "bar": "Here we set some value for bar"}

Not:
> Saving to 'my_model_collection'
>
> {"_id":42}

Writing that insert method for each my model seems to be against DRY, so what is correct/idiomatic way to achieve this in Go?

答案1

得分: 2

这是不可能的,详细信息请参见我的答案:https://stackoverflow.com/questions/30622605/can-embedded-struct-method-have-knowledge-of-parent-child/30629132#30629132

你可以做两件事:

1. 放弃方法,将其变为辅助/实用函数

这个想法是将Insert()BaseModel分离,将其变为一个简单的函数,并将要保存的文档传递给它。

我个人更喜欢这个选项,因为它需要更少的麻烦和维护。它可能看起来像这样:

func Insert(doc interface{}) {
    j, _ := json.Marshal(doc)
    fmt.Println(string(j))
}

你的标签中也有一个“拼写错误”:

type MyModel struct {
    *BaseModel
    Foo string `json:"foo"`
    Bar string `json:"bar"`
}

使用它:

Insert(m)

输出结果(在Go Playground上尝试):

{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}

2. 将(指向)包装器传递给BaseModel

在这种方法中,你必须传递一个指向嵌入结构体的指针,这样BaseModel.Insert()方法就会有一个指向它的指针,并可以使用它来保存/编组。这基本上是手动维护对嵌入我们的结构体的“引用”,并且正在保存/编组。

它可能看起来像这样:

type BaseModel struct {
    Id             int `json:"_id"`
    collectionName string

    wrapper interface{}
}

然后在Insert()方法中保存wrapper

func (m *BaseModel) Insert() {
    fmt.Printf("Saving to '%v'\n", m.collectionName)
    j, _ := json.Marshal(m.wrapper)
    fmt.Println(string(j))
}

创建稍微复杂一些:

func NewMyModel() *MyModel {
    mm := &MyModel{
        Foo: "Some value for foo",
    }
    mm.BaseModel = NewBaseModel("my_model_collection", mm)
    return mm
}

但输出结果与你期望的一样:

Saving to 'my_model_collection'
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}

Go Playground上尝试一下。

英文:

This is not possible, for details see my answer: https://stackoverflow.com/questions/30622605/can-embedded-struct-method-have-knowledge-of-parent-child/30629132#30629132

You may do 2 things:

1. Abandon method and make it a helper / utility function

The idea is to make Insert() detached from BaseModel and make it a simple function, and you pass the document to it which you want to save.

I personally prefer this option, as it requires less hassle and maintenance. It could look like this:

func Insert(doc interface{}) {
    j, _ := json.Marshal(doc)
    fmt.Println(string(j))
}

You also had a "typo" in the tags:

type MyModel struct {
	*BaseModel
	Foo string `json:"foo"`
	Bar string `json:"bar"`
}

Using it:

Insert(m)

Output (try it on the Go Playground):

{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}

2. Pass the (pointer to) the wrapper to the BaseModel

In this approach, you have to pass a pointer to the embedder struct so the BaseModel.Insert() method will have a pointer to it, and may use that to save / marshal. This is basically manually maintaining a "reference" to the struct that embeds us and is being saved/marshalled.

This is how it could look like:

type BaseModel struct {
	Id             int `json:"_id"`
	collectionName string

	wrapper interface{}
}

And then in the Insert() method save the wrapper:

func (m *BaseModel) Insert() {
	fmt.Printf("Saving to '%v'\n", m.collectionName)
	j, _ := json.Marshal(m.wrapper)
	fmt.Println(string(j))
}

Creation is slightly more complex:

func NewMyModel() *MyModel {
	mm := &MyModel{
		Foo: "Some value for foo",
	}
	mm.BaseModel = NewBaseModel("my_model_collection", mm)
	return mm
}

But output is as you wish:

Saving to 'my_model_collection'
{"_id":42,"foo":"Some value for foo","bar":"Here we set some value for bar"}

Try it on the Go Playground.

答案2

得分: 1

在Golang中,你不能覆盖父类的方法,因为这不是多态的工作方式。Insert方法将应用于BaseModel成员,而不是MyModel

此外,你试图以不正确的方式使用mgo。如果你想要在集合中插入文档,那么你已经有一个Insert方法用于Collection结构,它可以处理interface{}类型(与json.Marshal相同)。

当然,你可以有一个BaseModel,其中包含所有模型共享的字段。实际上,GORM使用了类似的方法,并提供了一个Model结构,用于包含在每个子模型中。

英文:

In Golang, you can't override a parent method, because that's not how polymorphism works. The Insert method will apply on the BaseModel member, and not on MyModel.

Also, you're trying to use mgo in an improper way. If you want to insert documents in collections, then you already have an Insert method for a Collection struct which works on interface{} types (same as json.Marshal).

Of course, you can have a BaseModel that will contain fields shared by all of your models. In fact, GORM uses a similar approach and provides a Model struct to be included in every child model.

答案3

得分: -1

众所周知的问题;o) 成员变量(如collectionName)以小写字母开头的名称在其他包(如json)中不可见。因此,将结构更改为:

type BaseModel struct {
    Id             int    `json:"_id"`
    CollectionName string `json:"collectionName"`
}

这样世界将变得更美好。

英文:

Well known problem ;o) Member variables (like collectionName) which name starts with lower letter are not visible from other packages (like json). Therefore change struct to:

type BaseModel struct {
	Id             int    `json:"_id"`
	CollectionName string `json:"collectionName"`
}

and world will be better place to live in.

huangapple
  • 本文由 发表于 2016年10月24日 17:47:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/40215326.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定