【问题标题】:Understanding polymorphism in Go理解 Go 中的多态性
【发布时间】:2018-12-07 17:34:19
【问题描述】:

我想我陷入了思考以下问题的多态性解决方案:

假设我有一个BaseTX struct,其中包含交易字段。现在我有两种特殊类型的交易:RewardTX structAllowanceTX struct

RewardTX struct此时只有BaseTX struct的组成。

AllowanceTX structBaseTX structAddField 组成。

我还有一个函数logicAndSaveTX(),它对BaseTX 的字段有一些逻辑,但最后是使用json.Marshal() 序列化整个对象并将byte[] 保存在某处。

type TXapi interface {
    logicAndSaveTX()
}

type BaseTX struct {
    Field1 string
    Field2 string
}

type RewardTX struct {
    BaseTX 
}

type AllowanceTX struct {
    BaseTX 
    AddField string
}

func (tx BaseTX) logicAndSaveTX() {
    // logic on BaseTX fields; simplified:
    tx.Field1 = "overwritten"
    tx.Field2 = "logic done"

    // here would be marshal to json and save; simplified to print object:
    fmt.Printf("saved this object: %+v \n", tx)
}

func SaveTX(tx TXapi) {
    tx.logicAndSaveTX()
}


func main() {
    rewardTX := RewardTX{BaseTX : BaseTX{Field1: "Base info1", Field2: "Base info2"}}
    SaveTX(rewardTX) // should print rewardTX with fields from BaseTX
    allowanceTX := AllowanceTX{BaseTX : BaseTX{Field1: "Base info1", Field2: "Base info2"}, AddField: "additional field"}
    SaveTX(allowanceTX) // would like to print allowanceTX with fields from BaseTX + AdditionalField >>> instead only printing fields from BaseTX
}

https://play.golang.org/p/0Vu_YXktRIk

我试图弄清楚如何实现结构和功能以对两种事务进行操作,但最后正确地序列化两种结构。我的问题是,AddField 在我当前的实现中没有出现。

也许我在这里有些脑残——我真的很想以“正确的 Go 方式”来实现它。 :)

【问题讨论】:

  • Go 没有像 Java 或 C++ 那样的 继承parent-child 类的经典定义中没有类层次结构。您可以获得的最接近的是接口,它就像一个只有抽象方法(没有字段)的抽象类。 Go 更喜欢 IMO 更简单、更容易追踪/阅读的组合。
  • 当有人试图通过嵌入来模拟继承时,他会失败并伤害自己。停止尝试:你会失败。将行为封装在一个接口中,你就有了多态性。

标签: oop go polymorphism


【解决方案1】:

Go 不是面向对象的。Go 中多态的唯一形式是接口。

来自其他面向对象的语言可能会很困难,因为您必须摆脱许多您可能会尝试继承的想法 - 例如,“基”类/类型之类的东西。只需从您的设计思维中删除“基础”;你试图把组合变成继承,那只会给你带来麻烦。

在这种情况下,也许你有一个合法的组合案例;您有多种类型使用的一些公共共享字段,但它不是“基本”类型。它可能是“元数据”或其他东西 - 鉴于您的示例非常抽象,我无法说出如何称呼它,但您明白了。

所以也许你有:

type TXapi interface {
    logicAndSaveTX()
}

type Metadata struct {
    Field1 string
    Field2 string
}

type RewardTX struct {
    Metadata 
}

func (tx RewardTX) logicAndSaveTX() {
    // logic on BaseTX fields; simplified:
    tx.Field1 = "overwritten"
    tx.Field2 = "logic done"

    // here would be marshal to json and save; simplified to print object:
    fmt.Printf("saved this object: %+v \n", tx)
}

type AllowanceTX struct {
    Metadata 
    AddField string
}

func (tx AllowanceTX) logicAndSaveTX() {
    // logic on BaseTX fields; simplified:
    tx.Field1 = "overwritten"
    tx.Field2 = "logic done"
    tx.AddField = "more stuff"

    // here would be marshal to json and save; simplified to print object:
    fmt.Printf("saved this object: %+v \n", tx)
}

如果元数据(或其他)字段的处理在所有用途中都是相同的,也许您可​​以为该类型提供自己的 logicTX 方法来填充这些字段,该方法可以由嵌入的结构的 logicAndSaveTX 调用它。

这里的关键是要将类型上的行为(方法)限定为该类型,而不是将其视为能够以某种方式对“子类型”进行操作。子类型不存在,并且 嵌入 在另一个类型中的类型无法对其容器进行操作。

【讨论】:

  • 我在回答的倒数第二段中提到了这一点。可行性取决于实际用例。
  • 非常感谢您的建议。我将尝试找出一种方法,将“通用逻辑”移动到两种类型的通用函数中,其余的在每种类型的单独函数中实现。我想我必须学会停止以严格的面向对象的方式思考:)
【解决方案2】:

这里还需要注意的是,Go 仅通过接口支持运行时多态。在 Golang 中编译时多态是不可能的。

来源:-https://golangbyexample.com/oop-polymorphism-in-go-complete-guide/

【讨论】:

    猜你喜欢
    • 2016-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多