サマリ
特定のModelに対して複数条件でsortしたい場合の実装方法についての考察
実装方法
- sortしたいfield毎にモデルにsortのinterfaceを実装をする - ➀
- embedで親のsortパッケージを継承する(sort条件を上書きする) - ➁
➀のとき
sortしたいStructのfield毎にLen,Swap,Lessのinterfaceを実装する。
古典的な実装方法で基本Structのfieldごとにsortで必要となるinterface3つを実装すればよい。
type Person struct{ Name Age } type PeopleByName []Person func (p PeopleByName)Len int{ return len(p) } func (p PeopleByName)Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Nameでsortする場合 func (p PeopleByName)Less (i, j int) { return p[i].Name < p[j].Name } func (p People)SortByName(){ sort.Sort(PeopleByName(p)) } type PeopleByAge []Person func (p PeopleByAge)Len int{ return len(p) } func (p PeopleByAge)Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Ageでsortする場合 func (p PeopleByAge)Less (i, j int) bool { return p[i].Age < p[j].Age } func (p People) SortByAge (){ sort.Sort(PeopleByAge(p)) } func main (){ p := []Person{ Person{ Name: "Taro", Age: 1, }, Person{ Name: "Jiro", Age: 2, }, Person{ Name: "Saburo", Age: 3 } // 名前でSort p.SortByName() // Jiro, Saburo, Taro // 年齢でsort p.SortByAge() // 1, 2, 3 }
古典的でわかりやすい反面、使わないinterfaceも定義してしまい、コード全体の見通しが悪い。
②の場合
ベースとなるStructにつき一度sortのinterface3つを実装する。
複数条件でsortする場合は、ベースとなるStructを埋め込んだ別のStructを作って、その新しく条件を追加するために作成したStructに必要なinterfaceのみを追加する。
ベースとなるStructを埋め込んだ、新しいStructではsortのinterfaceを全て実装しなくても、大本のStrcutでsortが実装されていれば、全て実装する必要はない。
必要となるinterfaceのみ実装すれば、sortがcallされる時にsort条件が上書きされる。
※ 継承みたいなイメージ
type Person struct{ Name Age } type People []Person func (p People)Len int{ return len(p) } func (p People)Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Nameでsortする場合 func (p People)Less (i, j int) bool { return p[i].Name < p[j].Name }
最初にベースとなるPeople StructにNameでsortするための基準となるsortのinterfaceを定義しておきます。
次にAgeでsortするために、Ageでsortする場合はNameでsortするために定義したLessを上書きします。
type PeopleByAge struct{ People } func (b PeopleByAge) Less (i, j int) bool { return b.People[i].Age < b.People[j].Age // ここでPeopleのLessを上書きしている。 } func main (){ p := []Person{ Person{ Name: "Taro", Age: 1, }, Person{ Name: "Jiro", Age: 2, }, Person{ Name: "Saburo", Age: 3 } // 名前でSort sort.Sort(p) // AgeでSort sort.Sort(PeopleByAge{p}) }
type PeopleByAge struct{ People }
の箇所でPeople Structを埋め込んでいるので、PeopleByAgeに新しく3つ全てのinterfaceを実装しなくても良くなります。
考察
古典的な方法はわかりやすい、かつ実装しやすいため基本➀で実装するのをベースに置きつつ、埋め込み型の方がよりsort条件を明確にでき、かつコードの記述量もすくないと思うので、使えるならStructに埋め込むパターンの方がいい気がする。
一方でsortについては➁の実装方法はベースのStructに依存することになるので、もしかしたら改修のときなどに予期せぬ影響が出る可能性がある。