Getting started #
In this tutorial, you learn how to use the singleton pattern in Go. We will first learn how to create a singleton, then we will see how a program with multiple goroutines can affect our singleton and how we can solve this problem using some standard Go packages.
Creating singleton #
Let's assume we are developing an application that requires an in-memory cache, we only need one instance of this cache throughout the application. Let's assume this cache needs to have a Get
and a Set
function, so it needs to fulfill the following interface.
package cache
// interface our cache needs to fullfill
type MyCache interface {
Get(key string) string
Set(key string, value string)
}
To fulfill, this interface let's create a struct called myCache
that implements the required functions.
package cache
type MyCache interface {
Get(key string) string
Set(key string, value string)
}
// š our cache struct
type myCache struct {
}
// the actual implementation of `Get` and `Set` don't matter for this tutorial
func (c myCache) Get(key string) string {
return "default value"
}
func (c myCache) Set(key, value string) {
}
Notice carefully that the name of myCache
starts with a lowercase letter, this is because we don't want anyone to create a new instance of myCache
.
Now it's time to write a GetCache
function that always returns the same instance of myCache
.
// ...continuing from the previous example
var cache *myCache
func GetCache() MyCache {
if cache == nil {
cache = &myCache{}
}
return *cache
}
Here we are checking if the cache
has not already been initialized i.e. cache
is nil
, then we initialize our cache and eventually return it. Once done we can use it throughout our application.
package main
import (
"fmt"
"<module_path>/cache"
)
func main() {
c := cache.GetCache()
fmt.Println(c.Get("key"))
}
Congratulations š you have now created a singleton. But hold on, your program is not safe in a concurrent environment š±.