Singleton (Одиночка)

Паттерн проектирования 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