はじめに
JavaでEnumを使った設計パターンはよくあるんですが、Goで同等のことをどうやるか考えてみました。
料金区分を例に
現場で役立つシステム設計の原則ではJavaのEnumを使った料金区分の設計と実装が紹介されています。
この例を参考にGoで適用するとどうなるか考えてみました。
まずは本と同様に料金区分をinterfaceで実装します。
type Fee interface {
Yen() int
Label() string
}
type adultFee struct{}
func (adultFee) Yen() int {
return 1500
}
func (adultFee) Label() string {
return "大人"
}
type childFee struct{}
func (childFee) Yen() int {
return 1000
}
func (childFee) Label() string {
return "子供"
}
料金区分(Fee)をinterface
大人料金(adultFee)と子供料金(childFee)はFeeのinterfaceを満たしています。
設計でJavaのEnumを使う魅力
- 定義したものだけを呼べて、定義していないものを呼び出すとコンパイルエラーになる。
- valueOfメソッドで文字列からenumの値を取得できる
GoでのEnum実装 - 変数で定義
Goで実装してみます。
var FeeType = struct {
Adult Fee
Child Fee
ValueOf func(s string) (fee Fee, err error)
}{
Adult: adultFee{},
Child: childFee{},
ValueOf: func(s string) (fee Fee, err error) {
switch s {
case "adult":
fee = adultFee{}
case "child":
fee = childFee{}
default:
err = fmt.Errorf("未定義の料金タイプ %s", s)
}
return
},
}
ポイントは定義したものだけを呼び出せるようにstructのメンバーとして定義済みの値を用意しました。
また、valueOfについてもstructのメンバーとして追加しています。
GoでのEnum実装 - 関数で定義
変数での定義が気になる方は、こんな方法も
func FeeType() struct {
Adult Fee
Child Fee
ValueOf func(s string) (fee Fee, err error)
} {
return struct {
Adult Fee
Child Fee
ValueOf func(s string) (fee Fee, err error)
}{
Adult: adultFee{},
Child: childFee{},
ValueOf: func(s string) (fee Fee, err error) {
switch s {
case "adult":
fee = adultFee{}
case "child":
fee = childFee{}
default:
err = fmt.Errorf("未定義の料金タイプ %s", s)
}
return
},
}
}
変数と違い関数なので間違っても上書きされないので安心です。
まとめ
今回はGoでJavaのEnum相当の機能を作り込む方法を考えてみました。
こんなやり方もあるよという方がいればコメント欄などで教えてもらえると嬉しいです。