【问题标题】:How to add a method for different structs which have one common field in golang如何为在golang中具有一个公共字段的不同结构添加一种方法
【发布时间】:2022-06-13 20:35:14
【问题描述】:

我正在为我的应用程序使用 beego/orm。这里我有 2 个模型

type ModelA struct {
    Guid string `orm:"pk"`
    FiledA string
}

type ModelB struct {
    Guid string `orm:"pk"`
    FiledB string
}

我需要为每个结构添加一个Save() 方法。一般来说,我可以创建一个Base 结构并将其混入ModelAModelB,但orm 不起作用。

有没有更好的解决方案?

edit1:在此处提供Save() 代码以使问题更清晰

func (this *ModelA) Save() error {
    o := orm.NewOrm()
    guid := guidlib.Generate()
    this.Guid = guid
    _, err := o.Insert(this)
    return err
}

func (this *ModelB) Save() error {
    o := orm.NewOrm()
    guid := guidlib.Generate()
    this.Guid = guid
    _, err := o.Insert(this)
    return err
}

【问题讨论】:

  • 我对beego/orm不是很熟悉,但是你不能在嵌入式结构上放置标签有什么原因吗?我仍然不确定这是否会真正帮助您,因为在该嵌入式结构上运行的方法将无法访问外部结构的任何其他字段。就而言
  • @AndrewN,所以在func (this *Base) Save() 方法中,this 变量总是指向Base,即使在ModelA 实例上调用它?也许基本解决方案是错误的方式。为了让问题更清楚,我添加了Save()方法的实现。
  • 正确。不过,this 在 Go 中并不是很惯用。如果逻辑确实完全相同相同,请将 Save 函数设为非方法,然后调用对象(通过接口)。让我看看我是否可以举个例子,我会把它作为答案发布,因为我正在超越 cmets...
  • Here 是一个非常简单的示例,使用接口编写您的 Save 逻辑一次。
  • @AndrewN,谢谢。如果interface可以包含数据字段,比如type IDer interface { ID string },代码会更简单漂亮。

标签: go beego


【解决方案1】:

是的。定义一个接口。另外,讨厌吹毛求疵,虽然我很确定您在谈论嵌入,但 Go 中不存在“混合”概念。这是一些演示构造的伪代码。

type Savable interface {
       Save()
}

// satisfies Savable for ModelA
func (a ModelA) Save() {
      // do something
}

var i Savable
i = SomeMethodThatRetunsMyModel()
i.Save()
SomeOthermMethodThatAcceptsASavableAndCallesSave(i)

嵌入方法:

type ModelA struct {
    ModelC
    FiledA string
}

type ModelB struct {
    ModelC
    FiledB string
}

type ModelC struct {
    Guid string `orm:"pk"`
}

func (this ModelC) Save() error {
    o := orm.NewOrm()
    guid := guidlib.Generate()
    this.Guid = guid
    _, err := o.Insert(this)
    return err
}

但是,请注意o.Insert(this) 不会插入任何未在ModelC 上定义的字段。正如我在下面的评论中提到的那样,在模型 A 和 B 将重新实现 Save 预先调用基类方法时可能使用的继承结构类型在 Go 中并不能很好地工作。

嵌入类型的方法解析规则并不完全清楚,可能会造成混淆。您可以在嵌入式结构中定义一个版本的Save,在嵌入器中重新定义它,甚至在该方法中调用它,但这并没有多大意义。如果您仍然必须静态引用嵌入类型,我会指出避免嵌入。例如,如果我有 ModelA 嵌入 ModelC 并且在更广泛的范围内我不得不做 ModelA.ModelC.SomeMethodThatIhaveToReferencExplicitlyToEnsureItsCalled() 那么我可能没有充分利用该功能。

【讨论】:

  • 重要的是要注意,虽然使用接口可能是一个好主意,但它不会使作者免于编写两个Save() 方法,每个结构一个。接口实际上只会帮助减少 使用 ModelA 和 ModelB` 的代码,因为它可以在接口上操作。
  • 感谢您指出embedding 不是mixin。正如@AndrewN 所说,它也会写两个Save() 方法,我只想写一次,因为保存ModelA 和保存ModelB 具有相同的逻辑。
  • @alxlx 嵌入也可能是有益的。您仍然需要使用接口来保持调用代码的通用性,但如果您愿意,您可以将所有常见字段移动到它们自己的结构中并将其嵌入。如果该数据子集足以满足您的需要,您可以在此处定义保存。如果您需要在此方法中同时编写共享数据和非共享数据,我通常建议您不要使用它,因为在 Go 中它是如何工作的并不像在您将继承、覆盖和调用基础的 C# 或 Java 中那样清楚方法。嵌入方法的解析变得混乱,我不喜欢这样。
  • @alxlx 我会用一个例子来编辑。我没有仔细阅读您的编辑,但如果该示例或多或少完整,那么我认为嵌入会很好。
【解决方案2】:

不,你不能这样做,因为 golang 不支持继承。但是您可以执行以下操作:

func Save(obj interface{}) error {
    o := orm.NewOrm()
    guid := guidlib.Generate()
    r := reflect.ValueOf(obj)
    f := reflect.Indirect(r).FieldByName("Guid") 
    f.setString(guid)  
    _, err := o.Insert(obj)
    return err
}

小心,如果没有“guid”字段会恐慌

【讨论】:

    猜你喜欢
    • 2019-05-20
    • 2016-06-05
    • 2019-10-29
    • 1970-01-01
    • 2013-08-10
    • 2019-01-21
    • 2015-10-21
    • 1970-01-01
    • 2023-02-23
    相关资源
    最近更新 更多