【问题标题】:Prevent Duplicate Functions in Go for Field-Only Structs防止 Go 中的重复函数用于仅限字段的结构
【发布时间】:2018-07-10 05:36:07
【问题描述】:

编辑:我更新了以下代码示例以更好地说明问题。

假设我有 2 个不需要任何功能的仅字段结构。

假设它们代表数据库中的 2 类相似数据:

type Boy struct {
    Name          string
    FavoriteColor string
    BirthDay      time.Time
}

type Girl struct {
    Name           string
    FavoriteFlower string
    BirthDay       time.Time
}

我为 Boy 结构体编写了一个函数,它根据给定的日期和男孩的信息打印问候语。

假设这是一个更复杂的函数的占位符,该函数基于time.Time 字段执行某些操作,并返回将在应用程序的其他地方使用的int

func CheckBirthDayBoy(date time.Time, boy Boy) int {
    numDays := 0

    if date.Before(boy.BirthDay) {
        // Greet how many days before birthday
        numDays = int(boy.BirthDay.Sub(date).Hours() / 24)
        fmt.Println("Hi, " + boy.Name + "! Only " + strconv.Itoa(numDays) + " days until your birthday! I hear your favorite color is " + boy.FavoriteColor + "!")
    } else if date.Equal(boy.BirthDay) {
        // Greet happy birthday
        fmt.Println("Happy birthday, " + boy.Name + "! I brought you something " + boy.FavoriteColor + " as a present!")
    } else {
        // Greet belated birthday
        numDays = int(date.Sub(boy.BirthDay).Hours() / 24)
        fmt.Println("Sorry I'm " + strconv.Itoa(numDays) + " days late, " + boy.Name + "! Here is something " + boy.FavoriteColor + " to cheer you up!")
    }

    return numDays
}

现在,由于 Go 是一种强类型语言,并且没有泛型,我最终不得不为 Girl 结构体编写一个重复的函数:

func CheckBirthDayGirl(date time.Time, girl Girl) int {
    numDays := 0

    if date.Before(girl.BirthDay) {
        // Greet how many days before birthday
        numDays = int(girl.BirthDay.Sub(date).Hours() / 24)
        fmt.Println("Hi, " + girl.Name + "! Only " + strconv.Itoa(numDays) + " days until your birthday! I hear your favorite flower is a " + girl.FavoriteFlower + "!")
    } else if date.Equal(girl.BirthDay) {
        // Greet happy birthday
        fmt.Println("Happy birthday, " + girl.Name + "! I brought you a " + girl.FavoriteFlower + " as a present!")
    } else {
        // Greet belated birthday
        numDays = int(date.Sub(girl.BirthDay).Hours() / 24)
        fmt.Println("Sorry I'm " + strconv.Itoa(numDays) + " days late, " + girl.Name + "! Here is a " + girl.FavoriteFlower + " to cheer you up!")
    }

    return numDays
}

有没有办法避免上述简单结构的代码重复?我不想为每个要实现它的新结构复制我的函数。

接口在这里不是一个选项,因为这两个结构都没有任何功能可言(并且为了满足接口而添加虚拟功能对我来说听起来像是一种倒退的解决方案)。

编辑:在考虑了我接受的解决方案之后,我现在相信接口也是解决这个问题的有效解决方案。感谢@ThunderCat 提出!

【问题讨论】:

  • 为什么不直接将time.Time 传递给CheckBirthday 函数?
  • @Hau Ma 上面的代码已经简化以说明问题。这也可能发生在更复杂的代码中。
  • 如何使用一个人而不是性别代码。更有意义。
  • @Derek 嗯...我可能需要修改我的代码示例以更好地说明问题。对困惑感到抱歉。将在几分钟内更新示例。
  • 是的,我建议提供一个尽可能接近问题的示例

标签: go code-duplication


【解决方案1】:

正如 ThunderCat 在 cmets 中提到的:将通用代码拉入单独的函数并调用。

func CheckBirthday(date, birthdate time.Time, name, gift string) (numDays int) {
    if date.Before(birthdate) {
        // Greet how many days before birthday
        numDays = int(birthdate.Sub(date).Hours() / 24)
        fmt.Printf("Hi, %s! Only %d days until your birthday! I hear your favorite is %s!\n", name, numDays, gift)
    } else if date.Equal(birthdate) {
        // Greet happy birthday
        fmt.Printf("Happy birthday, %s! I brought you a %s as a present!\n", name, gift)
    } else {
        // Greet belated birthday
        numDays = int(date.Sub(girl.birthday).Hours() / 24)
        fmt.Printf("Sorry I'm %d days late, %s! Here is a %s to cheer you up!\n", numDays, name, gift)
    }

    return
}

func CheckBirthdayBoy(date time.Time, boy Boy) int {
    return CheckBirthday(date, boy.BirthDay, boy.Name, boy.FavoriteColor)
}

func CheckBirthdayGirl(date time.Time, girl Girl) int {
    return CheckBirthday(date, girl.BirthDay, girl.Name, girl.FavoriteFlower)
}

【讨论】:

  • 请注意,有些文本读起来不太好,因为“这里有一个紫色让你振作起来”是无稽之谈,但技巧是正确的。
  • 是的,看起来这是我们最好的选择(因为当你的类型增加时类型切换和断言会变得非常疯狂),直到 Go 开发人员将泛型添加到语言中(这可能永远不会发生因为它确实倾向于增加抽象/复杂性)。感谢您和@ThunderCat 的回答!
  • @FloatingSunfish 来自 Python 和 Haskell 我同意。泛型非常强大,但也消除了许多可以在 Go 中保证的类型安全性(至少没有 Haskell 强大的类型系统!)
【解决方案2】:

类型转换对你有用吗?

func CheckBirthDay(date time.Time, i interface{}) int {
    switch v := i.(type) {
    case Boy:
      CheckBirthDay(date, v.Birthday)
    case Girl:
      CheckBirthDay(date, v.Birthday)
    default:
      DoSomething()
    }
}

func CheckBirthDay(date time.Time, bday time.Time) int {
    ...
}

【讨论】:

  • 我也想过这个,但没有打算使用它,因为从长远来看它会变得非常难以管理。不过,感谢您的建议!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多