如何在golang中定义动态“类型结构”?

3
这里是 Playground 链接 https://play.golang.org/p/qMKxqrOcc2。问题与 Playground 上的一个类似。
假设我有一个条件,需要执行以下操作:
if modelName == "a"{
    model = models.A
} 
else{
    model = models.B
}

其中AB是一些模型:

type A struct{
    filed1 string
    field2 string
    //etc

}

模型B

type B struct{
    filed1 string
    field2 string
    //etc

}

AB 中的字段有一些相同,但大多数反映了数据库表(文档),并且它们是相同类型(结构体类型)。

当我在所有人面前说:

var model interface{}

我收到了一个错误信息:
type models.A is not an expression 

我这样做是为了避免代码冗余,如果您问我为什么。
问题类似于此:如何在Golang中返回动态类型结构体? 以下是更新的代码:
b := c.mainHelper.GetModelBy("id", id, modelName).(map[string]interface{})
mapstructure.Decode(b, &model)

if modelName == "a"{
    model.Photos = []string{"ph1","ph2"}
}
if modelName == "b"{
    model.Docs = []string{"doc1","doc2"}
}

c.mainHelper.UpdateModel(product, id, modelName)

我知道这很愚蠢,也可能无法实现,但有没有办法做到这一点:

var model models.modelName --> somehow to concat modelName to this models?

这里有新的更新

我有两个模型:Post和Product。它们都有Photos字段。

type Post struct{

    Photos []string
    //etc
}

type Product {

    Photos []string
    //
}

现在我需要一个函数,它应该返回以下内容:
func () RemovePhotos(id string, modelName string){

//if modelName=="post"
    //get model post with id

//if modelName=="product"
    //get model product with id

//set model.Photos = []string
//update model in db
}

我理解无法分配类型,但如何使用此函数从不同类型中删除数据?据我所见,代码冗余将如下所示:

func () RemovePhotos(id string, modelName string) return bool{

    if modelName == "post"{

      var model models.Post
      modelWithdata := getModelWithId.(*model)
      modelWithdata.Photos = []string
      //update model in db here
    } 
    if modelName == "product"{
      var model models.Product
      modelWithdata := getModelWithId.(*model)
      modelWithdata.Photos = []string
      //update model in db here
    }

    //it does not matter what I return this is just redundancy example
    return true

}

你只需要关注变量模型的差异,即models.Post/var model models.Product。 这种代码冗余看起来不美观,但如果没有其他办法,那么好吧,我会接受这种带有冗余的代码。


赋值后model将如何使用?最佳解决方案将取决于此。 - Brian
5
“我这样做是为了避免代码冗余。” 不要太早避免冗余,直到你能看到哪些内容是多余的并可以被提取成(非空)接口之前,请先保留。然后再避免冗余。 - Volker
我无法接受任何答案,因为它们没有提供足够的答案。在我在这里提问之前,我已经尝试了所有之前答案中提到的方法。 - pregmatch
@pregmatch 我可以看一下你更新后的问题,但我更希望你将其作为一个单独的问题提出,考虑到已经有了相当多的活动,例如投票和评论可能不反映原始答案的新内容。如果你能将新内容从问题中剪切出来,并在这里放置一个新问题的链接,我一定会回答它 :)。 - Will C
3个回答

6

您不能指定类型。您必须指定实例。您的代码将有效地变成以下形式。我添加了两行注释,您需要更改这两行。

package main

import "fmt"

type B struct {
    filed1 string
    field2 string
    //etc

}

type A struct {
    filed1 string
    field2 string
    //etc

}

func main() {
    var model interface{}
    modelName := "b"
    if modelName == "a" {
        model = A{} // note the {} here
    } else {
        model = B{} // same here
    }

    fmt.Println(model)
}

仅提供一个建议,您可能不想使用通用的interface{}类型,而是最好使用实际接口,这两个接口都实现了AB。通用接口类型会带来更多麻烦,并且真正打败了使用静态类型语言Go的目的。


3
为了避免使用interface{},您可能需要了解interfaces(译注) - RickyA
@RickyA 同意,但根据他的问题,他是在问如何分配特定的模型而不是定义行为。当然,行为也可以添加到模型本身中以避免这些问题,但这真的取决于提问者的用例。 - Will C
是的,我只是添加了这个注释,因为接口是在不使用 interface{} 反射的情况下泛型化类型的唯一方法。如果他正在构建一个工厂,这可能会对他有所帮助。 - RickyA
@WillC 我认为你可以指出 interface{} 是 golang 中的一般类型,并将其与正确的 interface 区分开来。你表达的方式听起来像是 interface 不适合 golang 初学者使用。 - nevets
@WillC,你能看一下“这里有新的更新”吗? - pregmatch

2
你遇到这个错误是因为你试图将一个类型赋给interface{}实例,你需要赋一个实例。
如果你有以下代码;
var model interafce{}

if modelName == "a"{
    model = models.A{}
} 
else{
    model = models.B{}
}

那么它将正常工作。

这不起作用是因为当尝试执行类似 model.SomeFiled = "somevalue" 的操作时,它会显示类型 interface {} 没有 SomeFiled 字段或方法。 - pregmatch
@pregmatch 是的,interface{} 是 Go 中最通用的类型,它不会公开底层类型的任何字段或方法。您必须进行类型断言,例如 typeA := interfaceInstance.(A),才能访问 A 上的字段。 - evanmcdonnal
是的,你在这里提出的问题也是一个单独的问题。我相信@evanmcdonnal已经成功回答了你上面的问题:)。 - Will C
1
@pregmatch 可能不是不可能,但这很可能是对语言的错误抽象和使用案例。 - Will C
1
如果您提供一个更完整的例子,我可能会找时间来修复它。问题是,您的问题并没有说明各种事物的范围,只是一些断章取义的片段。您可能需要以不同的方式构建程序。 - evanmcdonnal
显示剩余3条评论

1
这是您从编辑器中得到的程序,具有接口类型实现:
package main

import (
    "log"
)

//// Interfaces ////
type PhotoManager interface {
    AddPhotos(id string) (bool, error)
}

//// Post ////
type Post struct {
    Photos []string
}

func (p *Post) AddPhotos(id string) (bool, error) {
    p.Photos = append(p.Photos, id)
    return true, nil
}

//// Product ////
type Product struct {
    Photos []string
    Docs   []string
}

func (p *Product) AddPhotos(id string) (bool, error) {
    p.Photos = append(p.Photos, id)
    return true, nil
}

// Useless function to demonstrate interface usage //
func AddPhotoToInterfaceImplementation(id string, pm PhotoManager) {
    pm.AddPhotos(id)
}

//// Main ////
func main() {
    post := Post{}
    product := Product{}
    post.AddPhotos("123")
    product.AddPhotos("321")
    AddPhotoToInterfaceImplementation("456", &post)
    AddPhotoToInterfaceImplementation("654", &product)
    log.Println(post)
    log.Println(product)
}

这里的关键部分包括:
  • 类型 PhotoManager interface,用于定义具有通用功能的接口
  • PostProduct 上的 AddPhotos 实现,提供接口函数的实际实现
  • pm PhotoManager 作为参数传递给 AddPhotoToInterfaceImplementation,展示了接口类型的使用方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接