generics - 一种处理嵌入一个通用结构的所有结构类型的方法(json 编码)

标签 generics model-view-controller go struct interface

我有一个带有 MVC 架构的 gin-gonic 网络应用程序。我创建了几个模型,它们都嵌入了一个通用结构:

type User struct {
   ID int
   Name string
}

type Admin struct {
   User
   Level int
}

... {
   User
}

现在我想将它们以 json 格式存储在数据库中。我想要实现的目标是只编写一个函数/方法,它将编码任何模型并将其保存到数据库中。此方法必须编码当前模型的所有字段,而不仅仅是来自用户结构,例如用户必须编码到 {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 复制/粘贴到每个模型文件,只更改方法接收器。这可以避免吗?

最佳答案

您可以像这样使用一个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 :

Getters

Go doesn't provide automatic support for getters and setters. There's nothing wrong with providing getters and setters yourself, and it's often appropriate to do so, but it's neither idiomatic nor necessary to put Get into the getter's name. If you have a field called owner (lower case, unexported), the getter method should be called Owner (upper case, exported), not GetOwner. The use of upper-case names for export provides the hook to discriminate the field from the method. A setter function, if needed, will likely be called SetOwner. Both names read well in practice:

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

关于generics - 一种处理嵌入一个通用结构的所有结构类型的方法(json 编码),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39131886/

相关文章:

php - 通过 ajax 调用 phalcon php MVC Controller

hash - Golang md5 Sum() 函数

file - 当有足够的文件句柄可用时,Go 程序用完文件句柄

java - Java 中的泛型方法

asp.net-mvc - ASP.NET MVC 和字符串不会给我编译时错误

java - 在带有泛型参数的泛型方法中使用 Spring RestTemplate

Java - 如何正确地将 ListSelectionListener 添加到 Controller ?

postgresql - 未应用过滤器(即空的“Where”子句)时,使用Golang网络表单从Postgres获取所有记录

java - 泛型的不兼容类型

algorithm - 通用排序函数接受 T,但要确保 T 是可比较的