Go is not a true object oriented programming language. The singleton pattern is an OOP design pattern, and hence it has no real applicability in Go.
With that caveat, however,
you can achieve what you can do with the singleton pattern in the OOP languages
simply using packages in Go.
A package
is a unit of encapsulation, and sharing, in the Go programming language.
Singleton Design Pattern in Go
If you are not familiar with the singleton pattern, here's a link to the Wikipedia article:
Note that you should not believe everything you see on the Internet. :) A lot of information provided in this article is not entirely correct (although the truth is never black and white).
In particular, the sample code in Go is not a real (idiomatic) Go code. To say the least, this kind of code is not very useful. Nobody uses this kind of program in real applications.
So, let's start from the beginning. Why do people use the singleton pattern in the first place?
One use case of the singletons is to limit instantiation of a certain type to only one instance (as the name implies). There can be many reasons why we want to do that. One common reason is because instantiation of an object is expensive, for example, when the type encapsulates a database connection, etc.
In Go, there is no way to achieve this. Struct types do not have constructors, let alone private constructors, and it is not possible to limit struct-based types to a single instance. (Note that Go's struct does not even support inheritance, and hence there is really no point of talking about the OOP singleton pattern.)
You have some more flexibility by using the interface-based types.
Another, and related, use case of the singletons is to have a shared state across the program. This is tantamount to using global variables, and many people call the singleton pattern an "anti-pattern" for this reason (among other things). This is a misguided criticism. All patterns are anti-patterns when they are not used in proper contexts.
In Go,
singletons (as broadly defined)
are used all the time.
A package
is often used as a singleton.
A package
is a singleton.
Here's an example. Again, this is not a "singleton" in the true OOP sense, but it essentially achieves (more or less) the same thing.
./singleton/vars.go
package singleton
import (
"errors"
)
var variables map[string]any
func init() {
variables = make(map[string]any)
}
func SetVar(key string, value any) {
variables[key] = value
}
func GetVar(key string) (any, error) {
if value, ok := variables[key]; ok {
return value, nil
}
return nil, errors.New("Not found: " + key)
}
func GetIntVar(key string) (int, error) {
if val, err := GetVar(key); err != nil {
return 0, err
} else {
if value, ok := val.(int); ok {
return value, nil
}
return 0, errors.New("Not int: " + key)
}
}
./main.go
package main
import (
"codeandtips/patt/singleton/singleton"
"fmt"
)
func main() {
key := "key1"
singleton.SetVar(key, 666)
val, err := singleton.GetIntVar(key)
if err == nil {
fmt.Println("Value =", val)
}
}