处理嵌入一个公共结构的所有结构类型的一种方法(json编组)

时间:2016-08-24 19:46:59

标签: generics model-view-controller go struct interface

我有一个带有点MVC架构的gin-gonic web应用程序。我创建了几个模型,所有模型都嵌入了一个常见的结构:

type User struct {
   ID int
   Name string
}

type Admin struct {
   User
   Level int
}

... {
   User
}

现在我想以json格式将它们存储在数据库中。我想要实现的目标是只编写一个将编组任何模型的函数/方法,并将其保存到DB中。此方法必须封送当前模型的所有字段,而不仅仅是来自User struct,例如,用户必须被整理到{id: 1, name: "zhora"},而管理员则会进入{id: 1, name: "gena", level: 2}

喜欢这个:

func (i *User) Save() {
  data, err := json.Marshal(i)
  check(err)
  if i.ID == 0 {
    _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
  } else {
    _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), i.ID)
  }
  check(err)
}

现在我必须将此func复制/粘贴到每个模型文件,只更改方法接收器。这可以避免吗?

1 个答案:

答案 0 :(得分:3)

你可以像这样使用一个func Save(d interface{})

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID   int
    Name string
}

type Admin struct {
    User
    Level int
}

func main() {
    Save(User{})
    Save(Admin{})
}

func Save(d interface{}) {
    body, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    st := string(body)
    fmt.Println(st)
}

输出:

{"ID":0,"Name":""}
{"ID":0,"Name":"","Level":0}

对于您的情况,请对所有类型使用此函数:

func Save(i interface{}, id int) {
    data, err := json.Marshal(i)
    check(err)
    if id == 0 {
        _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
    } else {
        _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), id)
    }
    check(err)
}

并称之为:

u := User{}
a := Admin{}

Save(u, u.ID)
Save(a, a.ID)

是的,这甚至简化了对Save到一个参数的调用:

package main

import (
    "encoding/json"
    "fmt"
)

type Model interface {
    ID() int
    setID(int)
}

type User struct {
    Id   int
    Name string
}

func (t User) ID() int      { return t.Id }
func (t User) setID(id int) { t.Id = id }

type Admin struct {
    User
    Level int
}

func main() {
    Save(User{})
    Save(Admin{})
}

func Save(d Model) {
    body, err := json.Marshal(d)
    if err != nil {
        panic(err)
    }
    st := string(body)
    fmt.Println(st)

    fmt.Println("ID is ", d.ID())
}

输出:

{"Id":0,"Name":""}
ID is  0
{"Id":0,"Name":"","Level":0}
ID is  0

现在您可以将这一功能用于所有类型:

func Save(i Model) {
    data, err := json.Marshal(i)
    check(err)
    id := i.ID()
    if id == 0 {
        _, err = app.DB.Exec(`INSERT INTO users(data) VALUES ($1) `, string(data))
    } else {
        _, err = app.DB.Exec(`UPDATE users SET data = $1 WHERE id=$2`, string(data), id)
    }
    check(err)
}

并称之为:

u := User{}
a := Admin{}

Save(u)
Save(a)

Effective Go

  

吸气剂

     

Go不为getter和setter提供自动支持。有   自己提供吸气剂和制定者并没有错,而且确实如此   通常适合这样做,但它既不是惯用的,也不是必需的   将Get放入getter的名字。如果您有一个名为owner的字段   (小写,未导出),getter方法应该称为Owner   (大写,导出),而不是GetOwner。使用大写名称   export提供了用于区分字段和方法的钩子。一个   如果需要,setter函数可能会被称为SetOwner。两个名字   在实践中读得很好:

owner := obj.Owner()
if owner != user {
    obj.SetOwner(user)
}