Singleton pattern is used to ensure that only an instance of a class is created. Each time an attempt to create an instance of the class will return the same instance.
NOTES
In this article, we are discussing the implementation of the Singleton Pattern in Go. Implementing the Singleton pattern in Go is slightly different from Java, PHP, and TypeScript.
See the Singleton in other languages in the “Other Code Implementations” section. Or, use the link below to check the details of the Singleton Pattern-
Implementation
There are a few points that we need to consider while implementing Singleton pattern in Go.
Common Key Points
These are the common key points for implementing Singleton pattern in Go.
Step #1: Simplest Singleton Implementation
This is the simplest approach we can take to implement Singleton pattern.
package main
import (
"fmt"
"log"
)
/** ---------- Singleton Implementation Start ---------- **/
// Define a struct for singleton
type singleton struct {
logPath string // Some data for the struct
}
// Declare an variable to hold the singeton instance
var singletonInstance *singleton
// Define a function that is responsible for generating singleton instance
func GetInstance(logPath string) *singleton {
// If there is no existing instance then generate one
if singletonInstance == nil {
log.Println("Generating new singleton instance")
singletonInstance = &singleton{logPath}
}
return singletonInstance
}
/** ---------- Singleton Implementation End ---------- **/
// Check the result in main
func main() {
// Generate one instance
singletonInstance1 := GetInstance("/path/abc")
fmt.Println(singletonInstance1.logPath)
// Try to generate another instance
singletonInstance2 := GetInstance("/some/changed/path")
fmt.Println(singletonInstance2.logPath)
// Check if the 2 instances are same or not
if singletonInstance1 == singletonInstance2 {
fmt.Println("Singleton Instance 1 and 2 are same instance") // This is the expected output
} else {
fmt.Println("Singleton Instance 1 and 2 are different")
}
}
GoOutput of the code will be as below:
/path/abc
/path/abc
Singleton Instance 1 and 2 are same instance
PlaintextStep #2: Use sync.Once
The instance generation part can be improved. We need to ensure thread safety for the instance generation part, as 2 threads can try to generate an instance at the same time.
For thread safety we can either generate the instance in “init()”, or we can use the “sync.Once”.
In the case of “init()” the instance will be generated, no matter if an instance is required or not. So, it is better to use “sync.Once”.
The following code is a small improvement over the previous code.
This will ensure the code responsible for the instance generation, is only executed once. So checking if the instance already exists, is not required in this case.
package main
import (
"fmt"
"log"
"sync"
)
/** ---------- Singleton Implementation Start ---------- **/
// Define a struct for singleton
type singleton struct {
logPath string // Some data for the struct
}
// Declare an variable to hold the singeton instance
var singletonInstance *singleton
// Declare once for using while generating singleton instance
var once sync.Once
// Define a function that is responsible for generating singleton instance
func GetInstance(logPath string) *singleton {
// Generate the instance only once
once.Do(func() {
log.Println("Generating new singleton instance")
singletonInstance = &singleton{logPath}
})
return singletonInstance
}
/** ---------- Singleton Implementation End ---------- **/
// Check the result in main
func main() {
// Generate one instance
singletonInstance1 := GetInstance("/path/abc")
fmt.Println(singletonInstance1.logPath)
// Try to generate another instance
singletonInstance2 := GetInstance("/some/changed/path")
fmt.Println(singletonInstance2.logPath)
// Check if the 2 instances are same or not
if singletonInstance1 == singletonInstance2 {
fmt.Println("Singleton Instance 1 and 2 are same instance") // This is the expected output
} else {
fmt.Println("Singleton Instance 1 and 2 are different")
}
}
GoIt will generate the same output. Output of the above code will be as below:
/path/abc
/path/abc
Singleton Instance 1 and 2 are same instance
PlaintextExamples
Let’s take a look at a few examples of Singleton pattern implementation in Go.
Example #1: Database Connection
Let’s implement the singleton pattern for representing a database connection.
Singleton Implementation
Follow the steps below to implement the connection struct as Singleton –
// connection.go
package main
import (
"fmt"
"log"
"sync"
)
// Struct for database connection information
type connection struct {
host string
port string
username string
password string
}
// Interface for connection related functions
type DbConnection interface {
PrintInfo()
ExecuteQuery(query string)
}
// Store connection reference
var dbConnectionInstance *connection
// To make sure instance generation code is executed only once
var once sync.Once
// Function for generating and getting database connection instance
func GetDbInstance(host, port, username, password string) *connection {
// Use once.Do to make sure the instance generation code is executed only once
once.Do(func() {
log.Println("Creating a new database instance")
// Create a new instance and store it in the dbConnectionInstance variable
dbConnectionInstance = &connection{host, port, username, password}
})
return dbConnectionInstance
}
// Utility function for demonestrating database connection information -- Dummy function
func (conn *connection) PrintInfo() {
fmt.Println("host:", conn.host)
fmt.Println("post:", conn.port)
fmt.Println("username:", conn.username)
fmt.Println("password:", conn.password)
}
// Utility function for database -- Dummy function
func (conn *connection) ExecuteQuery(query string) {
fmt.Println("Executing: ", query)
}
GoDemo
To demo the singleton pattern implementation use the following code.
Because of the Singleton implementation, those 2 instances will be the same.
// main.go
package main
import "fmt"
func main() {
// Create fist instance
dbInstance1 := GetDbInstance("localhost", "3306", "root", "complex#password")
dbInstance1.PrintInfo()
// Create second instance
dbInstance2 := GetDbInstance("localhost2", "2222", "root2", "complex#password2")
dbInstance2.PrintInfo()
// Compare the 2 instances. Both should be same, as both should refernece to the same instance
if dbInstance1 == dbInstance2 {
fmt.Println("dbInstance 1 and 2 are same instance") // This is the expected output
} else {
fmt.Println("dbInstance 1 and 2 are different")
}
}
GoOutput
The output of the above code will be as below.
host: localhost
post: 3306
username: root
password: complex#password
host: localhost
post: 3306
username: root
password: complex#password
dbInstance 1 and 2 are same instance
Tests
Let’s create test cases for testing the Singleton pattern implementation.
// connection_test.go
package main
import (
"reflect"
"testing"
)
func TestGetDbInstance(t *testing.T) {
// Create 2 instances with different param
connection1 := GetDbInstance("localhost", "1", "root1", "complex#password1")
connection2 := GetDbInstance("localhost2", "2", "root2", "complex#password2")
// Check if the 2 instances are the same or not
if connection1 != connection2 {
t.Error("Singleton database connnection does not return same instance")
}
// Compare with deep equal
if !reflect.DeepEqual(connection1, connection2) {
t.Error("Singleton instances has different values")
}
}
GoSource Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Database Connection | GitHub |
Other Code Implementations
Use the following links to check Singleton pattern implementation in other programming languages.