Golang 多态性/泛型嵌入

问题描述 投票:0回答:1

我想创建一组结构来设计应用程序。

在应用程序中,

  • 我们有一个包含许多字段的活动。
  • 每个字段可以是日期、时间、数字、文本或带有其他属性(如名称、描述)的 URL
  • 我们必须检查定义一个与每种字段关联的名为
    IsValid
    的函数,以检查用户为此字段发送的值是否属于该字段的类型,如果是,我们应该返回转换后的值

将这些需求转换成 Golang 是我来到这里的原因

这是我到目前为止所做的


type ActivityFieldText struct {
}

func (f ActivityFieldText) IsValid(value any) (string, bool) {
    return value.(string), true
}

type ActivityFieldDate struct {
    Layout string // Example of date
}

func (f ActivityFieldDate) IsValid(value any) (time.Time, bool) {
    d, err := time.Parse(f.Layout, value.(string))
    return d, err == nil
}

type ActivityFieldTime struct {
    Layout string // 12h AM/PM or 24h
}

func (f ActivityFieldTime) IsValid(value any) (time.Time, bool) {
    t, err := time.Parse(f.Layout, value.(string))
    return t, err == nil
}

type ActivityFieldNumber struct {
    Minimum *float64
    Maximum *float64
}

func (f ActivityFieldNumber) IsValid(value any) (float64, bool) {
    v, err := strconv.ParseFloat(value.(string), 32)
    if err != nil {
        return math.NaN(), false
    }

    if f.Maximum != nil && v > *f.Maximum {
        return math.NaN(), false
    }
    if f.Minimum != nil && v < *f.Minimum {
        return math.NaN(), false
    }

    return v, true
}

type ActivityFieldType interface {
    *ActivityFieldDate | *ActivityFieldTime | *ActivityFieldText | *ActivityFieldNumber

    // HOW TO DEFINE IT SO THAT IT RETURNS either 
    // time.Time when is *ActivityFieldDate or *ActivityFieldTime
    // string when is *ActivityFieldText
    // float64 when is *ActivityFieldNumber
    IsValid(value any) (interface{}, bool) 
}

type ActivityField[T ActivityFieldType] struct {
    Description string               `bson:"description" json:"description,omitempty"`
    Type        string               `bson:"type" json:"type,omitempty"`       // Text, Number, Date, Time, URL
    
    Details T
}

func NewActivityField[T ActivityFieldType](field T) *ActivityField[T] {
    return &ActivityField[T]{
        Details: field,
    }
}

type Activity struct {
    Id          primitive.ObjectID `bson:"_id" json:"id,omitempty"`
    Name        string             `bson:"name" json:"name,omitempty"`
    Description string             `bson:"description" json:"description,omitempty"`

    // Error here ... 
    // cannot use generic type ActivityField[T ActivityFieldType] without instantiationcompilerWrongTypeArgCount
    // ...
    // How to fix this error
    Fields      []ActivityField    `bson:"fields" json:"fields,omitempty"`
}


// Somewhere in the `main`, we should we use like this
values := make(map[string]any)
for _, field := range activity.Fields {
    value := input.Values[field.Code]
    castValue, ok := field.IsValid(value) // HERE it should not return interface{} nor any
    if ok {
        values[field.Code] = castValue
    }
}

请问,评论中写的错误如何解决?

go generics
1个回答
0
投票

我可能会做这样的事情:https://go.dev/play/p/2Dx5zt9vMJC

type Validator[T any] interface {
    Valid() (T, bool)
}

type Test1 struct{}

func (t Test1) Valid() (int, bool) {
    return 1, true
}

type Test2 struct{}

func (t Test2) Valid() (string, bool) {
    return "Test2", true
}

func IsValid[T any](t Validator[T]) (T, bool) {
    return t.Valid()
}

func main() {
    t1 := Test1{}
    t2 := Test2{}
    x, valid := IsValid(t1)
    fmt.Println(x, valid)
    y, valid := IsValid(t2)
    fmt.Println(y, valid)
}

© www.soinside.com 2019 - 2024. All rights reserved.