The subtleties of the Singleton implementation in Golang

Hi friends.





My name is Alex Versus and today we will take a look at the Singleton pattern , an implementation in the Golang language .





What's the point?

Singleton - refers to generative patterns. Guaranteed:





  • that the class / type only has one instance





  • provides a global access point to it.





What problem does it solve?

Let's talk about the problem that the template solves. A loner solves two problems at once, violating the Single Responsibility Principle (SRP) :





  1. Ensures that there is a single instance of the object. This is useful for accessing a shared resource, such as a database, or when implementing a single mechanism for changing a property, such as the sound level in an equalizer.

    Let's imagine that we have some kind of object and after a while you create another one, but you would like to receive not a new, but already created object. This behavior cannot be created using standard tools such as the constructor in object-oriented languages.





  2. Provide a global hotspot. Please note that this is not just a global variable through which you can reach a specific object. The global variable does not protect you from overwriting the created object.





, , . .





Golang?

GOlang? , , , -. . . - . :





Singleton class diagram
Singleton

GOlang . . getInstance()



? singleton



:





// declaration defined type 
type singleton struct {
   
}
      
      



singleton



, nil:





// declare variable
var instance *singleton = nil
      
      



instance



sync.Once



. , . Sigleton



:





// defined type with interface
type Singleton interface {
// here will be methods
}
      
      



:





// Get only one object
func GetInstance() Singleton {
   once.Do(func() {
      instance = new(singleton)
   })
   return instance
}
      
      



singleton



, :





// declaration defined type
type singleton struct {
   title string
}
      
      



Singleton



, :





// defined type with interface
type Singleton interface {
   SetTitle(t string)
   GetTitle() string
}


// Setter for singleton variable
func (s *singleton) SetTitle(t string) {
   s.title = t
}

// Getter singleton variable
func (s *singleton) GetTitle() string {
   return s.title
}
      
      



.





, . , :





package Singleton

import "testing"

func TestGetInstance(t *testing.T) {
   var s Singleton
   s = GetInstance()
   if s == nil {
      t.Fatalf("First sigletone is nil")
   }

   s.SetTitle("First value")
   checkTitle := s.GetTitle()

   if checkTitle != "First value" {
      t.Errorf("First value is not setted")
   }

   var s2 Singleton
   s2 = GetInstance()
   if s2 != s {
      t.Error("New instance different")
   }

   s2.SetTitle("New title")
   newTitle := s.GetTitle()
   if newTitle != "New title" {
      t.Errorf("Title different after change")
   }
}
      
      



:





go test -v -run TestGetInstance
=== RUN   TestGetInstance
--- PASS: TestGetInstance (0.00s)
PASS
ok      main/Singleton  0.310s
      
      



! , , . , , :





package Singleton

import (
   "fmt"
   "strconv"
   "sync"
   "testing"
)

func TestSecondGetInstance(t *testing.T) {
   s1 := GetInstance()
   s2 := GetInstance()

   var w sync.WaitGroup

   for i := 0; i < 3000; i++ {
      j := i
      w.Add(1)
      go func() {
         t := "title_" + strconv.Itoa(j)
         s1.SetTitle(t)
         w.Done()
      }()
      w.Add(1)
      go func() {


         t2 := "title_2_" + strconv.Itoa(j)
         s2.SetTitle(t2)
         w.Done()
      }()
   }

   fmt.Println(s1.GetTitle())
   fmt.Println(s2.GetTitle())
}
      
      



:





go test -v -run TestSecondGetInstance
=== RUN   TestSecondGetInstance
title_2998
title_2_2999
      
      



3000 , . , . , - , . ?





-ra



, . Golang, . .





. , Singleton. . , . , , : . , , . Go : sync.Mutex



sync.RWMutex



. :





// declaration defined type
type singleton struct {
   title string
   sync.RWMutex
}

// Setter for singleton variable
func (s *singleton) SetTitle(t string) {
   s.Lock()
   defer s.Unlock()
   s.title = t
}

// Getter singleton variable
func (s *singleton) GetTitle() string {
   s.RLock()
   defer s.RUnlock()
   return s.title
}
      
      



, :





go test -v -run TestSecondGetInstance
=== RUN   TestSecondGetInstance
--- PASS: TestSecondGetInstance (0.00s)
PASS
      
      



Singleton



Golang



.





?

  1. .





  2. .





  3. .





?

  1. . // , .





  2. .





  3. Golang. , .





  4. mock-. , . dummy.





Singleton — , , . , , , . - — Singleton, . (SRP), , . Singleton, . Singleton — , .





, , . - -, , .





. Golang. .

Alex Versus. !








All Articles