Goで共通の処理を実装した構造体を埋め込みたい
現在Goでyamlで書かれた設定ファイルを読み込むロジックを作成しています。
gopkg.in/yaml.v2
を使ってyamlファイルを読み込むと、map[interface{}]interface{}
が得られそれをmitchellh/mapstructure
でそれぞれのapp固有のconfig
構造体に変換しています。
コードは以下のようになっています。
baseapp.go
package common
import (
"gopkg.in/yaml.v2"
"io/ioutil"
)
// BaseApp is base of app.
type BaseApp struct {
}
// LoadConfig loads config
func (ba *BaseApp) LoadConfig(fpath string, config Config) (err error) {
b, err := ioutil.ReadFile(fpath)
if err != nil {
return
}
var c interface{}
if err = yaml.Unmarshal(b, &c); err != nil {
return
}
return config.ConvertMap(c)
}
config.go
package common
type Config interface {
ConvertMap(input interface{}) error
}
これらの使い方は以下のテストのようになっています。
package common
import (
"testing"
"github.com/mitchellh/mapstructure"
)
type SampleConfig struct {
Name string `yaml:"name"`
Number int `yaml:"number"`
Strings []string `yaml:"strings"`
}
//これは共通処理なので構造体ごとに実装したくない。(sc *SampleConfigがかわるだけ)
func (sc *SampleConfig) ConvertMap(input interface{}) error {
return mapstructure.Decode(input, &sc)
}
type SampleApp struct {
*BaseApp
}
func TestLoadConfig(t *testing.T) {
sampleApp := &SampleApp{}
var sampleConfig SampleConfig
err := sampleApp.LoadConfig("testdata/sample_config.yml", &sampleConfig)
if err != nil {
t.Fatal(err)
}
if sampleConfig.Name != "test name" {
t.Errorf("Name is not same as %s: %s", "test name", sampleConfig.Name)
}
if sampleConfig.Number != 100 {
t.Errorf("Number is not same as %d: %d", 100, sampleConfig.Number)
}
}
しかしこれでは、それぞれのconfig
このテストの例ではSampleConfig
ごとにConvertMap
の処理を実装しなければなりません。またConvertMap
の実装自体はそれぞれの構造体で同じになってしまいます。
これは無駄なのでどうにかしてそれぞれのconfig
でConvertMap
を実装する必要をなくしたいのですが、そのようなことは可能でしょうか。