
Давайте начнем с определения из Википедии.
“Одиночка (англ. 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‑ом и создать много экземпляров другими способами. А паттерн как раз и нужен для того, чтоб гарантированно исключить «не один экземпляр» в любом случае.
