简体   繁体   中英

Having a singleton pattern in Go Wire injection

I have a piece of code which is used to load configuration from file and parse it into a struct, I use this configuration quite often and hence I pass it around in the method parameters. Now I as my method parameters are increasing, I am looking at dependency injection and have settle with wire.

Now I have created a provider to load the configuration and an injector to provide the config struct. However each time I call the injection my file is read again, I want that the file is read once and the injection provided as many times as required without any additional loading.

Here is my provider:

// ProvideConfig config provider ...
func ProvideConfig() *config.FileConfig {
        var cfp string

        flag.StringVar(&cfp, "config", "config.json", "absolute path")
        flag.Parse()

    return config.Loadconfig(cfp)
}

Injector:

// GetConfig injector ...
func GetConfig() ConfigResource {
    wire.Build(ProvideConfig, NewConfigResource)
    return ConfigResource{}
}

Now when I call:

injection.GetConfig()

I see that ProvideConfig is called always. I can have a check in the provide config method the determine if the config is already loaded, I am not sure if there is a better way, something like a single instance loader which is built into the wire. I tried looking into the docs but could not find anything relevant.

As far as I'm aware, there's no built in way in wire to specify that a provider is a singleton / should only be called once.

This is accomplished in the usual way in Go, by using sync.Once . Your provider function can be a closure that does the expensive operation only once using sync.Once.Do . This is idiomatic in Go, and doesn't require any special provision from every library that wants to provide "single" loading.

Here's an example without wire:

type Value struct {
    id  int
    msg string
}

type ValueProvider func() *Value

// consumer takes a function that provides a new *Value and consumes
// the *Value provided by it.
func consumer(vp ValueProvider) {
    v := vp()
    fmt.Printf("Consuming %+v\n", *v)
}

// MakeSingleLoader returns a ValueProvider that creates a value once using an
// expensive operation, and then keeps returning the same value.
func MakeSingleLoader() ValueProvider {
    var v *Value
    var once sync.Once

    return func() *Value {
        once.Do(func() {
            v = ExpensiveOperation()
        })
        return v
    }
}

// ExpensiveOperation emulates an expensive operation that can take a while
// to run.
func ExpensiveOperation() *Value {
    return &Value{id: 1, msg: "hello"}
}

func main() {
    sl := MakeSingleLoader()
    consumer(sl)
    consumer(sl)
    consumer(sl)
}

If you're OK with the "singleton" value being a global, this code can be simplified a bit. Otherwise, it only calls ExpensiveOperation once, and keeps the value cached in a local inaccessible outside MakeSingleLoader .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM