Rotulador subrayando la palabra solución

La comunidad de desarrolladores de software está dividida en este debate: ¿conviene implementar el patrón de diseño Singleton en nuestras aplicaciones? O al contrario, ¿hay que evitarlo porque es un antipatrón?

Los detractores de Singleton argumentan así:

  • Las instancias Singleton están en el espacio global de la aplicación y eso es malo: utilizar variables globales es una mala práctica
  • Singleton introduce dependencias escondidas
  • Es difícil de probar con tests unitarios
  • Algunos desarrolladores lo implementan sin razón alguna, cuando sería mejor no hacerlo

Y los partidarios dicen:

  • Los argumentos anteriores no son concluyentes
  • Un patrón de diseño no debería dejar de implementarse por el solo hecho de utilizar una metodología de desarrollo de software determinada, como por ejemplo TDD
  • Tampoco hay para tanto, a Singleton se le tiene manía

Sea como fuere, si eres de los que por h o por b terminas utilizando varias clases Singleton en tu aplicación te acabarás encontrado con el siguiente problema.

Singleton solo se puede extender una vez

Este es el típico snippet de código Singleton que encontramos en sitios como PHP: The Right Way:

class Singleton
{
    private static $instance;

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    protected function __construct()
    {
    }

    private function __clone()
    {
    }

    private function __wakeup()
    {
    }
}

Pero solo va bien cuando lo hereda una sola clase PHP:

class Foo extends Singleton
{
  ...
}

No funciona si lo extienden varias clases: dos, tres o las que sean:

class Foo extends Singleton
{
  ...
}

class Bar extends Singleton
{
  ...
}

class FooBar extends Singleton
{
  ...
}

En tal caso se machacará el valor de la propiedad $instance porque solo puede alojar una instancia. Esto no es un problema de PHP, por cierto, también sucede en otros lenguajes de programación. Es una cuestión teórica sobre cómo está definido el patrón de diseño Singleton.

Pero ahora no discutamos cuestiones tan conceptuales, solucionemos el problema rápidamente:

abstract class Singleton
{
    private static $instances;

    private function __construct()
    {
    }

    final public static function getInstance()
    {
        $className = get_called_class();
        if (isset(self::$instances[$className]) == false) {
            self::$instances[$className] = new static();
        }
        return self::$instances[$className];
    }

    final private function __clone()
    {
        throw new Exception('Singleton cannot be cloned.');
    }

    final private function __wakeup()
    {
        throw new Exception('Singleton cannot be unserialized.');
    }
}

Este patrón Singleton modificado almacena un array de instancias en lugar de una sola, de modo que ya podemos extenderlo tantas veces como queramos. Yo lo he probado en un par de aplicaciones de producción, ¡y a mí me funciona sin ningún problema!

¿Te gusta? La idea la saqué de aquí, y si te interesa puedes ver la solución Java en este enlace.

Ejemplo práctico de Singleton extensible

Mira cómo queda la implementación Singleton modificada en esta app de GitHub. Es un web crawler real que sirve para analizar el contenido mixto de las webs. Fíjate que tiene tres clases (DB, Network y Crawler) que heredan de Singleton.

Esto es todo por hoy. Muchas gracias por haber leído este post, espero que te haya gustado y que lo compartas con tus amigos.

También te puede interesar leer esto...

Previous Post Next Post