The Singleton design pattern is one of the most important and frequently used in software programming. It ensures that a class has only a single instance during application runtime and provides a global access point to that instance. In this article, we will discuss the importance of Singleton, how to implement it in Golang, and the benefits it brings, especially in concurrent environments.
Singleton is a design pattern that restricts the instance of a class to a single instance. It is particularly useful in situations where a single point of control or a single shared resource is required, such as:
I'm going to list some points about the implementation from Pattern that make more sense and also to show that not everything is rosy, some of the problems we can have with it.
To implement a singleton I will use Golang. In this language we have to pay special attention to concurrency to ensure that only one instance is created, even when multiple goroutines try to access the instance simultaneously.
To bring our example closer to the real world, let's create a Logger for our application. A logger is a common tool in applications that needs to be unique to ensure log consistency.
First, we define the structure that we want to have a single instance.
package logger import ( "fmt" "sync" ) type Logger struct {} var loggerInstance *Logger
The NewInstance function is responsible for returning the single instance of the Singleton structure. We use a mutex to ensure security in concurrent environments, implementing double-checked locking for efficiency.
package logger import ( "fmt" "sync" ) type Logger struct{} var logger *Logger var mtx = &sync.Mutex{} func NewInstance() *Logger { if logger == nil { mtx.Lock() defer mtx.Unlock() if logger == nil { fmt.Println("Creating new Logger") logger = &Logger{} } } else { fmt.Println("Logger already created") } return logger }
A Log tool always has some log types, such as Info to just show the information, Error to show errors and so on. It's a way to also filter the type of information we want to show in our application.
So let's create a method that will show our log with the Info type. To do this we will create a function that will receive our log message and format it to INFO format.
package logger import ( "fmt" "sync" "time" ) const ( INFO string = "INFO" ) type Logger struct{} var logger *Logger var mtx = &sync.Mutex{} func NewInstance() *Logger { if logger == nil { mtx.Lock() defer mtx.Unlock() if logger == nil { fmt.Println("Creating new logger") logger = &Logger{} } } else { fmt.Println("Logger already created") } return logger } func (l *Logger) Info(message string) { fmt.Printf("%s - %s: %s\n", time.Now().UTC().Format(time.RFC3339Nano), INFO, message) }
And to use our new logger, we will instantiate it within our main package and create a log to see how this implementation works.
package main import ( "playground-go/pkg/logger" ) func main() { log := logger.NewInstance() log.Info("This is an example of log") }
This is the result when we run the program:
Creating new logger 2024-07-03T19:34:57.609599Z - INFO: This is an example of log
If we want to test whether NewInstance is really guaranteeing that we will only have one instance running, we can do the following test.
package main import ( "fmt" "playground-go/pkg/logger" ) func main() { log := logger.NewInstance() log.Info("This is an example of log") log2 := logger.NewInstance() log2.Info("This is another example of log") if log == log2 { fmt.Println("same instance") } else { fmt.Println("different instance") } }
Our logs have changed and now we can see that we blocked the creation of a new instance:
Creating new logger 2024-07-03T19:45:19.603783Z - INFO: This is an example of log Logger already created 2024-07-03T19:45:19.603793Z - INFO: This is another example of log same instance
The Singleton pattern is a powerful tool for ensuring that only one instance of a specific class exists during application runtime. In the logger example, we saw how this pattern can be applied to ensure log consistency across the application.
Hope this helps you understand Singleton in Golang better.
Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.
Copyright© 2022 湘ICP备2022001581号-3