ユーザーの都道府県を扱うアプリケーションを作成していたときに、
いままで
const (
PrefectureHokkaido = "hokkaido"
PrefectureAomori = "aomori"
:
)
というような県の定数をstring で宣言していていた。
この場合はUIに返すときには日本語に変換する必要があり愚直にmapを定義して変換していた。
var prefectureToJpName = map[string]string{
"hokkaido": "北海道",
"aomori": "青森県",
"iwate": "岩手県",
:
}
致命的な問題はなかったのだが、
- コードの量が増える
- 関連のあるものなのにコードが散らばってしまう
などの課題があった。
組み込み型として扱う
type Prefecture string
const (
Hokkaido Prefecture = "hokkaido"
Aomori Prefecture = "aomori"
:
)
上記のように型定義を行うことで一見変わらないように見えるが、組み込み関数を定義することができる。
つまり、いままでmap型で独立して存在していた日本語変換が
func (p Prefecture) ToJapanese() string {
switch s {
case Hokkaido:
return "北海道"
case Aomori:
return "青森"
:
}
}
というように拡張することができる。
Prefecture自体の実体はstring だが、ToJapanese という日本語変換の組み込み関数を持つ型になっている。
さらにこのように型を定義することによってさらに拡張ができるようになる。
例えば、県から県の所在グループを取得したいとなったときに、
:
type PrefectureGroup
const (
Hokkaido PrefectureGroup = "hokkaido"
Tohoku PrefectureGroup = "tohoku"
Kanto PrefectureGroup = "kanto"
:
)
func (p Prefecture) GetGroup() PrefectureGroup {
switch p {
case Hokkaido:
return "hokkaido"
case Aomori, Iwate, Miyagi, Akita, Yamagata, Fukushima:
return "tohoku"
:
}
}
というように拡張することができる。
PrefectureGroupも同様にToJpanaese などと拡張することもできる。
このように型として定義し、組み込み型で拡張することで
課題であった
- コードの量が増える
- 関連のあるものなのにコードが散らばってしまう
を解決でき、アプリケーションコードの見通しが良くなる。
なにより良いところはこのようなコードの書き方がより Goらしい ということだ。(Goらしいことでなにが嬉しいかというのは参考にもある実用Goにかかれています。)
補足
実際にプロダクトで都道府県を扱うとなると、
構造体として日本語なども格納しておくとより楽に扱えて良い。
https://github.com/diverse-inc/jp_prefecture
このリポジトリのように実装するのがベストに近い気がする。
参考
このtips は実用 Go言語 ―システム開発の現場で知っておきたいアドバイス 2.2.1 「メソッドを追加して組み込み型を拡張する」を参考に自分の体験を踏まえて書きました。