-1

在编写两种方法(一种用于切片,一种用于字符串映射)时,我意识到这两种方法的实现是相同的,唯一改变的是函数的原型。

我试图避免重复它,最初我想到了以下内容(参见 FIXME 部分):

package main

import (
    "fmt"
    "strings"
)

type SomethingList []*Something
type SomethingMap map[string]*Something

type Something struct {
    ID   string
    Type int
}

func (sl SomethingList) GetIDsString() string {
    return getIDsString(sl)
}

func (sm SomethingMap) GetIDsString() string {
    return getIDsString(sm)
}

func getIDsString(elements interface{}) string {
    var ids []string

    // FIXME: Yes, I know I can't iterate over an interface
    for element = range elements {
        ids = append(ids, element.ID)
    }
    return strings.Join(ids, ",")
}

func main() {
    s1 := Something{ID: "ABC", Type: 1}
    s2 := Something{ID: "DEF", Type: 1}

    sl := SomethingList{&s1, &s2}
    sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
    fmt.Println(sl.GetIDsString())
    fmt.Println(sm.GetIDsString())
}

重要的部分是函数getIDsString,它基本上采用结构的 ID 字段并将其内容连接到切片或映射的所有成员中。

在阅读了一些关于接口如何工作的信息后,我现在意识到(是的,我是 Go 的新手,这可能已经很明显了 :-))这是行不通的,因为 Go 是静态类型的,我不能简单地在运行时更改类型。接口也不允许我迭代。我已经能够使用一个循环来接近,该循环使用 reflect.ValueOf 和 Index() 上的 Len() 方法进行迭代以访问每个元素。但是 Index() 不适用于字符串映射。

在不复制大量代码的情况下解决这个问题的最惯用的方法是什么?

谢谢!

4

1 回答 1

1

一般来说,在 golang 中重复一小部分代码是很常见的。但是,如果您有大量重复代码,您可以在一个结构中拥有该逻辑,并临时将第二个结构转换为第一个以调用该逻辑:

package main

import (
    "fmt"
    "strings"
)

type (
    SomethingList []*Something
    SomethingMap  map[string]*Something

    Something struct {
        ID   string
        Type int
    }
)

func (sl SomethingList) GetIDsString() string {
    ids := make([]string, len(sl))
    for i := range sl {
        ids[i] = sl[i].ID
    }
    return strings.Join(ids, ",")
}

func (sm SomethingMap) GetIDsString() string {
    l := make(SomethingList, len(sm))
    i := 0
    for key := range sm {
        l[i] = sm[key]
        i++
    }
    return l.GetIDsString()
}

func main() {
    s1 := Something{ID: "ABC", Type: 1}
    s2 := Something{ID: "DEF", Type: 1}

    sl := SomethingList{&s1, &s2}
    sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
    fmt.Println(sl.GetIDsString())
    fmt.Println(sm.GetIDsString())
}

或者,您可以IDsString通过以下方式将创建与结构本身分离。

package main

import (
    "fmt"
    "strings"
)

type (
    SomethingList []*Something
    SomethingMap map[string]*Something

    Something struct {
        ID   string
        Type int
    }

    somethingIterator interface {
        ForEach(func(value Something))
    }
)

func (sl SomethingList) ForEach(f func(value Something)) {
    for i := range sl {
        f(*sl[i])
    }
}

func (sm SomethingMap) ForEach(f func(value Something)) {
    for key := range sm {
        f(*sm[key])
    }
}

func GetIDsString(iterator somethingIterator) string {
    var ids []string
    iterator.ForEach(func(value Something) {
        // Some sophisticated logic is here.
        ids = append(ids, value.ID)
    })
    return strings.Join(ids, ",")
}

func main() {
    s1 := Something{ID: "ABC", Type: 1}
    s2 := Something{ID: "DEF", Type: 1}

    sl := SomethingList{&s1, &s2}
    sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
    fmt.Println(GetIDsString(sl))
    fmt.Println(GetIDsString(sm))
}

第二种方法可以避免额外的中间结构创建,这可能有利于大列表/地图。

于 2021-07-25T21:04:03.133 回答