toniGitH /
Patron-Builder
Explicación del patrón Builder y ejemplo de uso en PHP
Loading repository data…
toniGitH / repository
Explicación del patrón Singleton y ejemplo de uso en PHP
Repositorio creado para explicar el patrón Singleton y su implementación mediante un ejemplo práctico en PHP.
El patrón Singleton es un patrón de diseño creacional que garantiza que una clase tenga una única instancia en toda la aplicación y proporciona un punto de acceso global a esa instancia.
El Singleton se utiliza cuando:
Ejemplos del mundo real:
Imagina que tienes una aplicación, con unos usuarios, y estos usuarios están sujetos a una única configuración de la aplicación común para todos los usuarios (número máximo de intentos de login, longitud de password, modo mantenimiento, etc...).
Podrías representar esa configuración como una clase que se encargara de gestionarla.
Como esa configuración DEBE ser común a todos los usuarios:
Debido a la naturaleza dinámica de una aplicación (parámetros de configuración, conexión a una base de datos, ...), que implica que ésta puede cambiar dinámicamente durante la ejecución de la aplicación, hace que tengamos que asegurarnos de que cuando haya cambios en esos parámetros, todos los elementos de la aplicación puedan ver esos cambios.
El Singleton asegura que solo exista uno y que todos lo compartan, con todas las ventajas que eso conlleva.
1. Constructor privado - ✅ IMPRESCINDIBLE
private function __construct() {}
¿Por qué? Sin esto, cualquiera puede hacer new MiClase() y tendrías múltiples instancias. Es OBLIGATORIO.
2. Propiedad estática privada que guarda la instancia - ✅ IMPRESCINDIBLE
private static ?MiClase $instancia = null;
¿Por qué? Necesitas un lugar donde guardar la única instancia. Es OBLIGATORIO.
3. Método estático público para obtener la instancia - ✅ IMPRESCINDIBLE
public static function obtenerInstancia(): MiClase
{
if (self::$instancia === null) {
self::$instancia = new self();
}
return self::$instancia;
}
¿Por qué? Es la única forma de acceder a la instancia. Es OBLIGATORIO.
4. Prevención de clonación - ⚠️ RECOMENDADO, pero NO obligatorio (buenas prácticas)
Evita que alguien haga:
$instancia1 = MiClase::obtenerInstancia();
$instancia2 = clone $instancia1; // Sin prevenir la clonación, esto crearía una copia de la instancia original
Es decir, que la clonación es un proceso que nos permitiría (una vez creada la instancia original del patrón singleton $instancia1, y de la que sólo queremos tener una y sólo una para toda la aplicación), crear una copia de dicha instancia original desde fuera de la clase Singleton, de forma que se rompería el patrón, porque tendríamos dos instancias diferentes de la misma clase.
Para evitar este problema, primero hay que entender cómo funciona el proceso de clonación en PHP.
Un objeto en PHP puede tener diferentes propiedades, y éstas pueden ser de diferentes tipos:
El operador clone
En el proceso de clonación, por un lado existe un operador llamado clone, que no es un método de PHP, sino un operador (como +, -, *, /, etc...), pero que realiza una operación algo más compleja que un simple operador: es capaz de copiar un objeto con todas sus propiedades, siempre que dichas propiedades sean valores primitivos o escalares y arrays de valores primitivos o escalares.
No es capaz de copiar como tal propiedades que sean objetos, ni objetos que contenga una prooiedad que sea un array. O mejor dicho, sí que copia la propiedad en sí, pero su valor, en este caso, ese objeto, no es un verdadero duplicado independiente, sino una referencia al objeto original, con lo que si modificamos el objeto original, también se modificará el objeto clonado, y viceversa.
A este tipo de clonado se le llama shallow copy, es decir, que copia el objeto con todas sus propiedades, pero que en el caso de esas propiedades que son objetos, no son verdaderamente objetos independientes de los originales, sino que mantienen la referencia al objeto original.
El método mágico __clone()
Por otro lado existe el método __clone(), que sí es un método, concretamente es un método mágico nativo de PHP que se ejecuta automáticamente cuando utilizamos el operador clone, y que por defecto está vacío, es decir, no tiene implementación.
Este método __clone() está diseñado como vacío, y su propósito es que pueda ser reescrito dentro de una determinada clase, conteniendo instrucciones para copiar sus propiedades que sean objetos.
Es decir, que dado que el operador clone no hace una verdadera copia de aquellas propiedades que sean objetos, PHP nos proporciona un método "autoejecutable" o hook (que se ejecuta al usar el operador clone), para que incluyamos dentro de él todas aquellas instrucciones que consideremos oportunas para que estas propiedades objetos sí se clonen realmente (como valores independientes respecto de los del objeto original).
A este tipo de clonado, en el que se implementa el método __clone() para copiar también las propiedades que sean objetos, se le llama deep copy.
Por tanto, podríamos decir que el proceso de clonación se divide en dos fases: "shallow copy" y "deep copy".
El proceso de clonación
Lo importante que hay que entender es que, en el proceso de clonación, tanto el operador clone como el método mágico __clone siempre ACTÚAN EN CONJUNTO.
Cuando llamamos al operador clone, durante el proceso de clonación que se inicia, se va a comprobar si el método __clone es ACCESIBLE::
__clone() existe y es public, el proceso de clonado se ejecutará sin problemas, ejecutándose la shallow copy más la deep copy (se copiarán las propiedades objeto que se hayan implementado en el método __clone())__clone() no existe, el proceso de clonado también se ejecutará sin problemas, pero sólo se ejecutará la shallow copy (no se copiarán las propiedades objeto puesto que el método __clone(), al no existir, no implementará la copia de las propiedades objeto)__clone() existe pero es private o protected, el proceso de clonado fallará, y no se ejecutará ninguna de las fases.Dado que, por lo visto anteriormente, el clonado "falla" cuando el método __clone() existe y es private o protected, la manera más segura para evitar la clonación es implementar, DENTRO de la propia clase Singleton, el método __clone() , como método privado:
private function __clone() {}
De esta forma, no se podrá clonar la instancia original del patrón singleton desde fuera de la clase singleton:
$instancia1 = MiClase::obtenerInstancia();
$instancia2 = clone $instancia1; // ❌ ERROR: Cannot access private method __clone()
Evidentemente, SÍ podrías clonar la instancia original del patrón singleton desde dentro de la propia clase Singleton, pero en este caso, estarías rompiendo el patrón tú mismo intencionadamente, lo que no tendría sentido.
5. Prevención de deserialización - ⚠️ RECOMENDADO, pero NO obligatorio (buenas prácticas)
¿Por qué se recomienda evitar la deserialización?
Evita que alguien haga:
$instancia = MiClase::obtenerInstancia();
$serializado = serialize($instancia);
// ...y más tarde...
$instancia2 = unserialize($serializado); // Sin prevenir la deserialización, esto crearía una copia de la instancia original
La serialización es el proceso de convertir un objeto (o una estructura de datos) en una cadena de texto (string), con el objetivo de poder:
En PHP, el ejemplo típico es:
$cadena = serialize($objeto);
Y la deserialización es el proceso contrario:
$objeto = unserialize($cadena);
No se puede serializar cualquier objeto ni se puede deserializar cualquier cadena de texto (existen unos límites).
En la serialización interviene el método mágico __sleep(), y en la deserialización interviene el método mágico __wakeup().
Para mantener la integridad de un Singleton, la serialización no es un problema, porque sólo convierte nuestra instancia en una cadena de texto, pero no crea una nueva instancia. Sin embargo, la deserialización SÍ es un problema, porque al deserializar la cadena de texto, SÍ se crearía una nueva instancia de la clase singleton, lo que rompería el Singleton.
Por eso, si queremos proteger un Singleton ante este problema, debemos actuar sobre la deserialización, es decir, sobre el método __wakeup(), que es el que se ejecuta cuando se deserializa un objeto. Debemos definirlo en la clase Sing
Selected from shared topics, language and repository description—not editorial ratings.
toniGitH /
Explicación del patrón Builder y ejemplo de uso en PHP
toniGitH /
Explicación del patrón Prototype y ejemplo de uso en PHP
toniGitH /
Explicación del patrón Decorator y ejemplo de uso en PHP
toniGitH /
Explicación del patrón Facade y ejemplo de uso en PHP
toniGitH /
Explicación del patrón Adapter y ejemplo de uso en PHP
toniGitH /
Explicación del patrón Abstract Factory y ejemplo de uso en PHP