This is the second article of the series, wherein we will be discussing one of the easiest but beneficial design patterns, Singleton. Many times we need an object to be instantiated once and used again and again. This is the niche use case of the Singleton design pattern. In terms of Golang, which has no classes or objects, singleton means having only one copy of struct that coordinates actions throughout the code.
Carrying on with the example already discussed in the previous post, Suppose you are implementing a project that interacts with Database. You have to interact with the Database at more than one instance in your code (let us say n instances). Now before any kind of interaction you need a connection with Database. But think carefully, do you need n different connections for the n instances you are interacting with the database? The simple answer is NO. You can easily reuse the same connection at all the n instances. Thus you need to a kind of mechanism where the connection object is created only once and used at all the n instances.
Let us try implementing this use case in Golang and see how it goes. Please note that the implementation in Goalng differs from other programming languages since it is not OOP language. In languages that support OOP, a static member, private constructor and public accessor method serves the need of implementing Singleton design pattern.
Let us have a connection package that exposes conn as a singleton struct that determines the connection of the database with the code. As any variable whose name starts with small is private in Golang, we will be keeping conn in smalls. This will inhibit the other packages to use the conn struct directly.
package connection type conn struct { creds map[string]string } func (c *conn) Sett () { //Setter Function } func (c *conn) Get () { //Getter Function }
We made the struct that we want to be a singleton, i.e, instantiate only once. If you look closely, we cannot let any other package directly access the type conn
and that is the reason we have made it private. But we still have 2 issues :
- We need to provide some public mechanisms to other packages to access the conn type.
- We need to make sure that all the other packages use the same copy of the conn struct.
To solve the second issue we can make use of pointers, we can let other packages use pointers that refer to the same struct conn
. In order to solve the first issue, we need to expose a public function that returns a pointer to the conn
struct.
var c *conn func NewConnection () *conn { if c== nil { c = &conn { creds : make(map[string]string) } } return c }
We create a global variable c
as a pointer
to conn
(accessible only in the package) and expose a function NewConn
to return c
. Inside the function, we see if conn has already been instantiated, if no we instantiate it and return a pointer
to it.
This solution looks to be good enough. There is no way an external package can make a duplicate copy of conn
. But is it safe under all considerations? A little brainstorming gives you the answer, NO.
This solution is not thread safe. Multiple goroutines will mess the code up as for every Goroutine there will be a new conn
instantiated. What can we do here? We can use synchronization. Golang is shipped with a sync package that can be used to solve this issue.
var (
c *conn
once sync.Once
)
func NewConn() *conn {
once.Do(func() {
c= &conn{
creds: make(map[string]string),
}
})
return c
}
The singleton design pattern is a very simple but powerful pattern. It has great applications and is very to easy implement. Most of us have been using this without even knowing what it is. We shall be covering more patterns in the later articles of the series.
0 Comments