Singleton (Одиночка)

Паттерн проектирования Singleton
Описание Singleton
В приложении экземпляр определённого класса должен присутствовать гарантировано в одном экземпляре.
По сути создаётся статический экземпляр класса: защищённый от клонирования, обычного инстанцирования через конструктор, и других способов получения ссылки на единственный экземпляр — кроме статического метода-конструктора.
Примеры реализации
// Singleton Pattern in JavaScript
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this.value = Math.random();
Singleton.instance = this;
}
getValue() {
return this.value;
}
}
// Usage
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true
console.log(instance1.getValue() === instance2.getValue()); // true
// Singleton Pattern in C++
#include <iostream>
#include <mutex>
class Singleton {
private:
static Singleton* instance;
static std::mutex mutex_;
int value;
Singleton() : value(rand()) {}
public:
Singleton(Singleton &other) = delete;
void operator=(const Singleton &) = delete;
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mutex_);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
int getValue() const {
return value;
}
};
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex_;
// Singleton Pattern in Go
package main
import (
"fmt"
"sync"
)
type Singleton struct {
value int
}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{value: 42}
})
return instance
}
func (s *Singleton) GetValue() int {
return s.value
}
// Usage
func main() {
instance1 := GetInstance()
instance2 := GetInstance()
fmt.Println(instance1 == instance2) // true
fmt.Println(instance1.GetValue() == instance2.GetValue()) // true
}
# Singleton Pattern in Python
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
cls._instance.value = 42
return cls._instance
def get_value(self):
return self.value
# Usage
if __name__ == "__main__":
instance1 = Singleton()
instance2 = Singleton()
print(instance1 is instance2) # True
print(instance1.get_value() == instance2.get_value()) # True
<?php
// Singleton Pattern in PHP
class Singleton {
private static $instance = null;
private $value;
private function __construct() {
$this->value = rand();
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getValue() {
return $this->value;
}
private function __clone() {}
private function __wakeup() {}
}
// Usage
$instance1 = Singleton::getInstance();
$instance2 = Singleton::getInstance();
var_dump($instance1 === $instance2); // bool(true)
var_dump($instance1->getValue() === $instance2->getValue()); // bool(true)
?>
За и против
Singleton часто называют «анти-паттерном», т.к. при использовании он несёт следующие проблемы:
- Статический метод получения экземпляра создаёт жесткую связь, игнорируя Инъекцию зависимостей.
- Приложение неявно обладает неким состоянием.
- «Одиночку» проблематично заменить в тестовом окружении.
Эти причины снижают тестируемость кода.
Поскольку это один из самых простых для понимания шаблонов, его часто используют новички, при этом злоупотребляя им. В то же время, для некоторых задач, когда нужен некий глобальный объект, и простейший способ доступа к нему из любой точки приложения (например Service Locator) самым простым решением является Singleton.
UML-диаграмма создана в программе Dia