Singleton Design Pattern

The Singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global access point to it.

In software development, there are situations where we need to restrict the instantiation of a class to a single object. This is particularly useful when dealing with shared resources or managing global state.

The Singleton pattern is implemented by defining a class with a private constructor and a static method. The first time the static method is called, it creates a new instance of the class. In subsequent calls, it returns the existing instance.

The Singleton pattern provides the following benefits:

  • Ensures that there is only one instance of a class in the application.
  • Provides a global access point to that instance.
  • Delays the initialization of the instance, improving performance and resource utilization.

The following are two implementations of the Singleton pattern in Go, each demonstrating how to connect to a database:

Hungry Singleton

In Go language, the Hungry Singleton pattern is a design pattern that creates and initializes an object at program startup. Here is how to rewrite the getDB() function using the Hungry Singleton pattern:

package main

import (
	"fmt"
	"sync"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Member struct {
	ID   string `gorm:"primaryKey;size:16"`
	Name string `gorm:"size:255"`
	Age  int64  `gorm:"size:10"`
}

var db *gorm.DB

func init() {
	fmt.Println("init")
	db = func() *gorm.DB {
		dsn := "user:pw@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
		db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
		if err != nil {
			panic("Failed to connect to database: " + err.Error())
		}
		fmt.Println("Connection to database!")
		return db
	}()
}

func getDB() *gorm.DB {
	return db
}

func main() {
	fmt.Println("main")
	Db := getDB()
	fmt.Println("addr", &Db, Db)
	var members = Member{ID: "1"}
	Db.Find(&members)
	fmt.Println(members)

	Db2 := getDB()
	fmt.Println("addr", &Db2, Db2)
	var members2 = Member{ID: "2"}
	Db2.Find(&members2)
	fmt.Println(members2)
}

Lazy Singleton

In Go language, the Lazy Singleton pattern is a design pattern that creates and initializes an object only when it is first needed. Here is how to rewrite the getDB() function using the Lazy Singleton pattern. The InitDB method on the InfoDb struct is used to initialize the database connection. It uses the sync.Once object o to ensure that the database connection is only initialized once, even if InitDB is called multiple times.

package main

import (
	"fmt"
	"sync"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Member struct {
	ID   string `gorm:"primaryKey;size:16"`
	Name string `gorm:"size:255"`
	Age  int64  `gorm:"size:10"`
}

var (
	db *gorm.DB
	o  sync.Once
)

func getDB() *gorm.DB {
	o.Do(func() {
		db = func() *gorm.DB {
			dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
			db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
			if err != nil {
				panic("Failed to connect to database: " + err.Error())
			}
			fmt.Println("Connection to database!")
			return db
		}()
	})
	return db
}

func main() {
	fmt.Println("main")
	Db := getDB()
	fmt.Println("addr", &Db, Db)
	var members = Member{ID: "1"}
	Db.Find(&members)
	fmt.Println(members)

	Db2 := getDB()
	fmt.Println("addr", &Db2, Db2)
	var members2 = Member{ID: "2"}
	Db2.Find(&members2)
	fmt.Println(members2)
}