Patrón Singleton
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 71 de 91.
El Patrón Singleton es un patrón de diseño creacional que garantiza que una clase tenga una única instancia a lo largo del ciclo de vida de su aplicación. También proporciona un punto de acceso global a esa instancia.
Este patrón es útil cuando necesitas exactamente un objeto para coordinar acciones en todo el sistema, como una conexión a la base de datos, un gestor de configuración o un registrador (logger). Crear múltiples instancias de estos desperdiciaría recursos o causaría un comportamiento inconsistente.
Aquí tienes cómo implementar un Singleton en PHP:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {
// El constructor privado evita la instanciación directa
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query(string $sql): string {
return "Executing: $sql";
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
echo $db1 === $db2 ? "Same instance" : "Different instances";
Resultado:
Same instanceEl patrón se basa en tres elementos clave: un constructor privado que impide el uso de new directamente, una propiedad estática que mantiene la instancia única y un método estático que crea la instancia en la primera llamada y la devuelve en las llamadas posteriores.
Para que el Singleton sea verdaderamente a prueba de balas, también debes evitar la clonación y la deserialización:
<?php
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
Desafío
FácilVamos a construir un gestor de configuración utilizando el Patrón Singleton. En aplicaciones reales, normalmente se desea una única fuente de verdad para los ajustes de configuración; tener múltiples objetos de configuración podría provocar un comportamiento inconsistente en todo el sistema.
Organizarás tu código en dos archivos:
Config.php— Crea una claseConfigque implemente el Patrón Singleton. Tu gestor de configuración debe:- Almacenar los ajustes en una propiedad de array privada
- Tener un constructor privado que inicialice el array de ajustes como vacío
- Proporcionar un método estático
getInstance()que devuelva la instancia única - Evitar la clonación haciendo que
__clone()sea privado - Incluir un método
set(string $key, string $value): voidpara almacenar un ajuste - Incluir un método
get(string $key): ?stringque devuelva el valor de una clave, onullsi la clave no existe - Incluir un método
getAll(): arrayque devuelva todos los ajustes
main.php— Incluye el archivo Config. Recibirás dos entradas: una clave y un valor.Obtén la instancia de Config y establece un ajuste predeterminado con la clave
"app_name"y el valor"MyApp".Obtén una segunda referencia a la instancia de Config (usando
getInstance()de nuevo) y úsala para establecer otro ajuste utilizando la clave y el valor de las entradas.Finalmente, verifica que el Singleton esté funcionando correctamente:
- Imprime
"Same instance: true"o"Same instance: false"basándote en si ambas referencias apuntan al mismo objeto (usa===para comparar) - Imprime
"Settings count: [count]"mostrando el número total de ajustes almacenados (usacount()en el resultado degetAll()) - Imprime
"App name: [value]"mostrando el ajuste app_name recuperado a través de la segunda referencia
- Imprime
Este desafío demuestra el beneficio principal del Patrón Singleton: no importa cuántas veces llames a getInstance(), siempre obtendrás el mismo objeto, asegurando que todas las partes de tu aplicación compartan el mismo estado de configuración.
Hoja de referencia
El Patrón Singleton asegura que una clase tenga una única instancia a lo largo del ciclo de vida de su aplicación y proporciona un punto de acceso global a dicha instancia.
Este patrón es útil para objetos como conexiones a bases de datos, gestores de configuración o loggers, donde múltiples instancias desperdiciarían recursos o causarían un comportamiento inconsistente.
Implementación básica de Singleton:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {
// El constructor privado evita la instanciación directa
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
echo $db1 === $db2 ? "Same instance" : "Different instances";
// Salida: Same instance
Elementos clave:
- Constructor privado — evita el uso de
newdirectamente - Propiedad estática — contiene la instancia única
- Método estático — crea la instancia en la primera llamada y la devuelve en las llamadas posteriores
Previniendo la clonación y la deserialización:
<?php
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
Pruébalo tú mismo
<?php
require_once 'Config.php';
// Leer entrada
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));
// TODO: Obtener la instancia de Config y establecer "app_name" a "MyApp"
// TODO: Obtener una segunda referencia a Config usando getInstance() de nuevo
// TODO: Usar la segunda referencia para establecer el ajuste usando $key y $value de la entrada
// TODO: Verificar que el Singleton está funcionando:
// - Imprimir "Same instance: true" o "Same instance: false" basado en la comparación ===
// - Imprimir "Settings count: [count]" mostrando el total de ajustes
// - Imprimir "App name: [value]" mostrando app_name recuperado a través de la segunda referencia
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy