Есть ли Singleton в Golang?

e1fce193a367bcdd081500385457864a.png

Давайте начнем с определения из Википедии.

«Одиночка (англ. Singleton) — порождающий шаблон проектирования, гарантирующий, что в однопоточном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.»

«Единственный экземпляр некоторого класса» означает что нет возможности написать код, в котором объект может быть скопирован или создан еще каким-либо способом.

В этом посте про «гарантирующий».

Рассмотрим общеизвестную реализацию. Поле в структуре — для лучшей визуализации результата.

type Singleton struct {

	id int

}

var (

	instance *Singleton

	once     sync.Once

)

func GetInstance() *Singleton {

	once.Do(func() {

		instance = &Singleton{1}

	})

	return instance

}

Протестируем его.

func main() {

	// first object

	s1 := GetInstance()

	fmt.Printf("type: %T, value: %v, ptr: %p\n", s1, s1, s1)

 	// second object. Copying

	s2 := *s1

	s2.id = 2

	fmt.Printf("type: %T, value: %v, ptr: %p\n", s2, s2, &s2)

	// third object   

	s3 := new(Singleton)

	s3.id = 3

	fmt.Printf("type: %T, value: %v, ptr: %p\n", s3, s3, s3)

	// many different objects

	for i := 4; i < 8; i++ {

		s := Singleton{i}

		fmt.Printf("type: %T, value: %v, ptr: %p\n", s, s, &s)

	}

}

Результат:

type: *main.Singleton, value: &{1}, ptr: 0xc0000b4000

type: main.Singleton, value: {2}, ptr: 0xc0000b4018

type: *main.Singleton, value: &{3}, ptr: 0xc0000b4020

type: main.Singleton, value: {4}, ptr: 0xc0000b4030

type: main.Singleton, value: {5}, ptr: 0xc0000b4038

type: main.Singleton, value: {6}, ptr: 0xc0000b4040

type: main.Singleton, value: {7}, ptr: 0xc0000b4048

Вывод: создано неограниченное количество объектов!

Немного рассуждений.

Почему-то реализация паттерна в Go указывает программисту пользоваться функцией GetInstance ().

А как следует из определения паттерна это должно быть не словесное правило, а сама реализация типа не должна позволять создать более одного экземпляра.

Если использовать словесные правила для программистов, то реализация паттерна могла бы быть и такой: «Не создавайте более одного экземпляра.» И код писать не надо.)

Сравним с реализацией на языке С++.

Все конструкторы и оператор копирования или спрятаны в private секции, или удалены. Или сразу оба способа.

В секции public только один метод — GetInstance(). Нет никаких вариантов создать более одного объекта.

class Singlеton

{

public:

        static Singleton& GetInstance()

        {

                static Singleton theSingleInstance;

                return theSingleInstance;

        }

private:         

        Singleton(){}

        Singleton(const Singleton& root) = delete;

        Singleton& operator=(const Singleton&) = delete;

};

Совсем другая картина в Go.

В тесте видно, что легитимными способами возможен не «единственный экземпляр некоторого класса».

«Для создания объекта используйте GetInstance ()» — слабый аргумент. Программист может не знать что этот тип должен быть Singleton‑ом и создать много экземпляров другими способами. А паттерн как раз и нужен для того, чтоб гарантированно исключить «не один экземпляр» в любом случае.

© Habrahabr.ru