This page provides practical implementation examples of the Singleton pattern across multiple programming languages. Each example demonstrates thread-safe implementation and follows language-specific best practices.
C# Implementation
C# offers multiple ways to implement Singleton. Here’s a thread-safe implementation using lazy initialization:
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance
{
get { return lazy.Value; }
}
private Singleton()
{
// Initialize resources
Console.WriteLine("Singleton instance created");
}
public void DoSomething()
{
Console.WriteLine("Singleton method called");
}
}
// Usage
class Program
{
static void Main()
{
// Both variables reference the same instance
Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // Output: True
s1.DoSomething();
}
}
Key Points:
- Uses
Lazy<T>
for thread-safe lazy initialization - The
sealed
keyword prevents inheritance - Private constructor prevents external instantiation
- .NET Framework handles thread safety automatically
Node.js (JavaScript/TypeScript) Implementation
JavaScript implementation using ES6 classes and module pattern:
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
// Initialize properties
this.timestamp = Date.now();
console.log('Singleton instance created');
Singleton.instance = this;
}
doSomething() {
console.log(`Singleton method called at ${this.timestamp}`);
}
static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
// Freeze the instance to prevent modifications
Object.freeze(Singleton);
// Usage
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // Output: true
instance1.doSomething();
// Alternative: Using Module Pattern
const DatabaseConnection = (() => {
let instance;
function createInstance() {
return {
connect: () => console.log('Connected to database'),
query: (sql) => console.log(`Executing: ${sql}`)
};
}
return {
getInstance: () => {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// Usage of module pattern
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // Output: true
Key Points:
- JavaScript is single-threaded, so no explicit thread safety needed
- Module pattern provides a cleaner alternative
- ES6 modules are singletons by default when exported
Object.freeze()
prevents modifications
Python Implementation
Python offers several approaches. Here’s a thread-safe implementation using metaclasses:
import threading
class SingletonMeta(type):
"""
Thread-safe Singleton metaclass implementation
"""
_instances = {}
_lock = threading.Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
def __init__(self):
self.value = None
print("Singleton instance created")
def do_something(self):
print(f"Singleton method called with value: {self.value}")
# Usage
if __name__ == "__main__":
# Both variables reference the same instance
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # Output: True
s1.value = "Hello"
print(s2.value) # Output: Hello
s1.do_something()
# Alternative: Using decorator
def singleton(class_):
instances = {}
def get_instance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return get_instance
@singleton
class DatabaseConnection:
def __init__(self):
self.connected = False
print("Database connection initialized")
def connect(self):
self.connected = True
print("Connected to database")
# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Output: True
Key Points:
- Metaclass approach provides elegant Singleton implementation
- Threading lock ensures thread safety
- Decorator pattern offers a simpler alternative
- Python’s module system makes modules singletons by default
Go Implementation
Go implementation using sync.Once for thread-safe initialization:
package main
import (
"fmt"
"sync"
)
type Singleton struct {
value string
}
var instance *Singleton
var once sync.Once
// GetInstance returns the singleton instance
func GetInstance() *Singleton {
once.Do(func() {
fmt.Println("Creating singleton instance")
instance = &Singleton{
value: "singleton",
}
})
return instance
}
func (s *Singleton) DoSomething() {
fmt.Printf("Singleton method called with value: %s\n", s.value)
}
func main() {
// Both variables reference the same instance
s1 := GetInstance()
s2 := GetInstance()
fmt.Println(s1 == s2) // Output: true
s1.value = "modified"
fmt.Println(s2.value) // Output: modified
s1.DoSomething()
}
// Alternative: Using init function for eager initialization
var eagerInstance = &Singleton{
value: "eager singleton",
}
func GetEagerInstance() *Singleton {
return eagerInstance
}
Key Points:
sync.Once
guarantees the initialization code runs only once- Thread-safe without explicit locking
- Package-level variables provide eager initialization alternative
- Idiomatic Go approach using package structure
C++ Implementation
Modern C++11 and later provides thread-safe static local variable initialization:
#include <iostream>
#include <mutex>
#include <memory>
class Singleton {
private:
static std::mutex mutex_;
std::string value_;
// Private constructor
Singleton(const std::string value) : value_(value) {
std::cout << "Singleton instance created\n";
}
// Delete copy constructor and assignment operator
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
// Thread-safe in C++11 and later
static Singleton& getInstance(const std::string& value = "default") {
static Singleton instance(value);
return instance;
}
void doSomething() const {
std::cout << "Singleton method called with value: "
<< value_ << "\n";
}
std::string getValue() const {
return value_;
}
void setValue(const std::string& value) {
value_ = value;
}
};
// Initialize static member
std::mutex Singleton::mutex_;
// Usage
int main() {
// Both variables reference the same instance
Singleton& s1 = Singleton::getInstance("First");
Singleton& s2 = Singleton::getInstance("Second");
std::cout << "Same instance? "
<< (&s1 == &s2 ? "Yes" : "No") << "\n";
s1.setValue("modified");
std::cout << "s2 value: " << s2.getValue() << "\n";
s1.doSomething();
return 0;
}
// Alternative: Using smart pointers for more complex scenarios
class ModernSingleton {
private:
static std::unique_ptr<ModernSingleton> instance_;
static std::once_flag initFlag_;
ModernSingleton() {
std::cout << "ModernSingleton instance created\n";
}
public:
static ModernSingleton& getInstance() {
std::call_once(initFlag_, []() {
instance_.reset(new ModernSingleton());
});
return *instance_;
}
ModernSingleton(const ModernSingleton&) = delete;
ModernSingleton& operator=(const ModernSingleton&) = delete;
};
std::unique_ptr<ModernSingleton> ModernSingleton::instance_;
std::once_flag ModernSingleton::initFlag_;
Key Points:
- C++11 guarantees thread-safe static local variable initialization
- Delete copy constructor and assignment operator to prevent copies
std::call_once
provides explicit thread-safe initialization- Modern C++ prefers references over pointers for singleton access
Rust Implementation
Rust implementation using lazy_static or once_cell crate for thread-safe initialization:
use std::sync::{Arc, Mutex};
use once_cell::sync::Lazy;
// Using once_cell for lazy static initialization
static SINGLETON: Lazy<Arc<Mutex<Singleton>>> = Lazy::new(|| {
println!("Creating singleton instance");
Arc::new(Mutex::new(Singleton {
value: String::from("singleton"),
}))
});
struct Singleton {
value: String,
}
impl Singleton {
fn get_instance() -> Arc<Mutex<Singleton>> {
Arc::clone(&SINGLETON)
}
fn do_something(&self) {
println!("Singleton method called with value: {}", self.value);
}
fn set_value(&mut self, value: String) {
self.value = value;
}
}
fn main() {
// Both variables reference the same instance
let s1 = Singleton::get_instance();
let s2 = Singleton::get_instance();
println!("Same instance? {}", Arc::ptr_eq(&s1, &s2));
{
let mut instance = s1.lock().unwrap();
instance.set_value(String::from("modified"));
instance.do_something();
}
{
let instance = s2.lock().unwrap();
println!("s2 value: {}", instance.value);
}
}
// Alternative: Using std::sync::Once for more control
use std::sync::Once;
struct ConfigManager {
config: String,
}
static mut CONFIG_MANAGER: Option<ConfigManager> = None;
static INIT: Once = Once::new();
impl ConfigManager {
fn get_instance() -> &'static ConfigManager {
unsafe {
INIT.call_once(|| {
println!("Initializing ConfigManager");
CONFIG_MANAGER = Some(ConfigManager {
config: String::from("default config"),
});
});
CONFIG_MANAGER.as_ref().unwrap()
}
}
fn get_config(&self) -> &str {
&self.config
}
}
// Usage in another function
fn use_config() {
let config1 = ConfigManager::get_instance();
let config2 = ConfigManager::get_instance();
println!("Config: {}", config1.get_config());
println!("Same instance? {}", std::ptr::eq(config1, config2));
}
Key Points:
once_cell
crate provides safe lazy static initializationArc<Mutex<T>>
ensures thread-safe shared ownership and mutability- Rust’s ownership system prevents many common Singleton pitfalls
std::sync::Once
provides low-level initialization control- Cargo.toml dependency:
once_cell = "1.19"
Comparison Table
Language | Thread Safety Mechanism | Complexity | Best For |
---|---|---|---|
C# | Lazy<T> | Low | Enterprise applications |
Node.js | Single-threaded (no explicit locking) | Very Low | Web applications |
Python | threading.Lock or metaclass | Medium | Data processing, scripting |
Go | sync.Once | Low | Concurrent systems |
C++ | Static local variables (C++11+) | Medium | System programming |
Rust | once_cell + Arc<Mutex> | Medium-High | Safe concurrent systems |
Testing Singleton Implementations
Here’s a general approach to test Singleton implementations across languages:
# Python example - similar concepts apply to other languages
import threading
import unittest
class TestSingleton(unittest.TestCase):
def test_single_instance(self):
"""Test that only one instance is created"""
s1 = Singleton()
s2 = Singleton()
self.assertIs(s1, s2)
def test_shared_state(self):
"""Test that state is shared between references"""
s1 = Singleton()
s2 = Singleton()
s1.value = "test"
self.assertEqual(s2.value, "test")
def test_thread_safety(self):
"""Test thread-safe initialization"""
instances = []
def create_instance():
instances.append(Singleton())
threads = [threading.Thread(target=create_instance)
for _ in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
# All instances should be the same
self.assertTrue(all(inst is instances[0] for inst in instances))
if __name__ == '__main__':
unittest.main()
Common Pitfalls and Solutions
- Serialization Issues: Deserializing a singleton can create new instances. Implement custom serialization methods to return the existing instance.
- Reflection Attacks: In languages like Java and C#, reflection can break singleton. Use enums (Java) or sealed classes with proper protection.
- Testing Difficulties: Singletons make unit testing hard. Consider dependency injection or using interfaces to allow mock implementations.
- Thread Safety Overhead: Don’t add thread safety if your application is single-threaded. Choose eager initialization for simple cases.
Best Practices Summary
- Always consider thread safety in multi-threaded environments
- Make constructors private to prevent external instantiation
- Delete or disable copy constructors and assignment operators
- Use language-specific idioms (Lazy<T> in C#, sync.Once in Go, etc.)
- Document why Singleton is used and its lifecycle
- Consider lazy vs eager initialization based on your use case
- Implement proper cleanup if resources need to be released
These implementations provide solid foundations for using the Singleton pattern in production code. Remember to adapt them based on your specific requirements and always consider whether Singleton is the right pattern for your use case.