How to avoid re-implementing sort.Interface for similar golang structs

后端 未结 3 1292
执念已碎
执念已碎 2021-02-06 03:30

There is one problem bothering me in Golang. Say I have 2 structs:

type Dog struct {
   Name string
   Breed string
   Age int
}

type Cat struct {
    Name stri         


        
3条回答
  •  春和景丽
    2021-02-06 03:38

    This Specific Case

    In this specific case you shouldn't use 2 different types as they are identical, just use a common Animal type:

    type Animal struct {
        Name string
        Age  int
    }
    
    func (a Animal) String() string { return fmt.Sprintf("%s(%d)", a.Name, a.Age) }
    
    type SortAnim []*Animal
    
    func (c SortAnim) Len() int           { return len(c) }
    func (c SortAnim) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
    func (c SortAnim) Less(i, j int) bool { return c[i].Age < c[j].Age }
    
    func main() {
        dogs := []*Animal{&Animal{"Max", 4}, &Animal{"Buddy", 3}}
        cats := []*Animal{&Animal{"Bella", 4}, &Animal{"Kitty", 3}}
    
        fmt.Println(dogs)
        sort.Sort(SortAnim(dogs))
        fmt.Println(dogs)
    
        fmt.Println(cats)
        sort.Sort(SortAnim(cats))
        fmt.Println(cats)
    }
    

    Output (Go Playground):

    [Max(4) Buddy(3)]
    [Buddy(3) Max(4)]
    [Bella(4) Kitty(3)]
    [Kitty(3) Bella(4)]
    

    General Case

    In general you can only use a common sorting implementation if you're willing to give up concrete types and use interface types instead.

    Create the interface type you want your slice to hold:

    type Animal interface {
        Name() string
        Age() int
    }
    

    You can have a common implementation of this:

    type animal struct {
        name string
        age  int
    }
    
    func (a *animal) Name() string  { return a.name }
    func (a *animal) Age() int      { return a.age }
    func (a animal) String() string { return fmt.Sprintf("%s(%d)", a.name, a.age) }
    

    Your specific animal types:

    type Dog struct {
        animal  // Embed animal (its methods and fields)
    }
    
    type Cat struct {
        animal // Embed animal (its methods and fields)
    }
    

    You implement sort.Interface on SortAnim:

    type SortAnim []Animal
    
    func (c SortAnim) Len() int           { return len(c) }
    func (c SortAnim) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
    func (c SortAnim) Less(i, j int) bool { return c[i].Age() < c[j].Age() }
    

    Using it:

    dogs := SortAnim{&Dog{animal{"Max", 4}}, &Dog{animal{"Buddy", 3}}}
    cats := SortAnim{&Cat{animal{"Bella", 4}}, &Cat{animal{"Kitty", 3}}}
    
    fmt.Println(dogs)
    sort.Sort(SortAnim(dogs))
    fmt.Println(dogs)
    
    fmt.Println(cats)
    sort.Sort(SortAnim(cats))
    fmt.Println(cats)
    

    Output (Go Playground):

    [Max(4) Buddy(3)]
    [Buddy(3) Max(4)]
    [Bella(4) Kitty(3)]
    [Kitty(3) Bella(4)]
    

提交回复
热议问题