防止在 Go 中为 Field-Only 结构重复函数

Prevent Duplicate Functions in Go for Field-Only Structs

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

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

假设它们代表数据库中两种类型的相似数据:

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

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

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

假设这是一个更复杂的函数的占位符,该函数基于 time.Time 字段和 returns 将在应用程序的其他地方使用的 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 提出来!

类型转换对你有用吗?

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 {
    ...
}

正如 ThunderCat 在评论中提到的:将公共代码拉到一个单独的函数中并调用。

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)
}