Patrón Singleton: Una Instancia Global Controlada

¿Qué es el Patrón Singleton?
El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a ella. Es especialmente útil cuando necesitas controlar el acceso a recursos compartidos como conexiones de bases de datos, pools de conexiones o configuraciones de aplicación.
classDiagram
class Singleton {
-instance: Singleton$
-Singleton()
+getInstance(): Singleton$
+operation()
}
note for Singleton "instance es estática\ngetInstance() es estático\nConstructor es privado"Cuándo Usar Singleton
Usar Singleton:
- Conexiones a bases de datos
- Configuración de aplicación
- Pools de conexiones
- Loggers centralizados
Evitar Singleton:
- En contextos con múltiples threads sin sincronización adecuada
- Cuando dificulta las pruebas unitarias
- Si introduce acoplamiento global innecesario
Implementación Paso a Paso
1. Constructor Privado
El constructor privado previene la instanciación externa directa con new:
class DatabaseConnection {
private connectionString: string;
private constructor(connectionString: string) {
this.connectionString = connectionString;
}
}2. Propiedad Estática para la Instancia
La clase mantiene una referencia estática a su única instancia:
private static instance: DatabaseConnection | null = null;3. Método de Acceso (getInstance)
Punto de acceso global que crea la instancia solo si no existe (Lazy Initialization):
public static getInstance(connectionString?: string): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection(
connectionString ?? 'mongodb://localhost:27017/default'
);
}
return DatabaseConnection.instance;
}4. Método Reset para Testing
Permite resetear la instancia en pruebas unitarias:
public static resetInstance(): void {
DatabaseConnection.instance = null;
}Ejemplo Práctico Completo
interface User {
id: string;
name: string;
}
class DatabaseConnection {
private static instance: DatabaseConnection | null = null;
private connectionString: string;
private connected: boolean = false;
private constructor(connectionString: string) {
this.connectionString = connectionString;
}
public static getInstance(connectionString?: string): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection(
connectionString ?? 'mongodb://localhost:27017/default'
);
}
return DatabaseConnection.instance;
}
public static resetInstance(): void {
DatabaseConnection.instance = null;
}
async connect(): Promise<void> {
if (this.connected) return;
console.log(`Connecting to ${this.connectionString}...`);
this.connected = true;
}
async query<T>(sql: string): Promise<T[]> {
console.log(`Executing: ${sql}`);
return [] as T[];
}
}
// Uso
const db1 = DatabaseConnection.getInstance('mongodb://prod:27017/app');
const db2 = DatabaseConnection.getInstance();
console.log(db1 === db2); // true - misma instancia
await db1.connect();
const users = await db2.query<User>('SELECT * FROM users');Variante: Singleton con Symbol
Para garantizar unicidad incluso entre diferentes módulos:
const SINGLETON_KEY = Symbol.for('app.singleton.config');
class ConfigurationManager {
private config: Map<string, unknown> = new Map();
static get instance(): ConfigurationManager {
const globalAny = globalThis as Record<symbol, ConfigurationManager>;
if (!globalAny[SINGLETON_KEY]) {
globalAny[SINGLETON_KEY] = new ConfigurationManager();
}
return globalAny[SINGLETON_KEY];
}
set<T>(key: string, value: T): void {
this.config.set(key, value);
}
get<T>(key: string): T | undefined {
return this.config.get(key) as T;
}
}Resumen
| Aspecto | Descripción |
|---|---|
| Propósito | Una única instancia con acceso global |
| Componentes | Constructor privado, instancia estática, método getInstance |
| Beneficios | Control de recursos, estado compartido consistente |
| Consideraciones | Puede dificultar testing, usar resetInstance para pruebas |
Conclusión
El patrón Singleton es fundamental para gestionar recursos compartidos de forma controlada. Úsalo con prudencia: es poderoso pero puede introducir acoplamiento global si se abusa de él.
En el próximo artículo exploraremos el Patrón Factory Method, que delega la creación de objetos a subclases especializadas.
Basado en "Design Patterns: Elements of Reusable Object-Oriented Software" (Gang of Four).