2009-11-30 48 views
18

Si tuviera una clase de fábrica que crea nuevos objetos de algún tipo, y que la clase factroy es un producto único, de esta manera:Extender la clase Singleton abstracta

class Database_Factory extends Base_Factory { 
    private static $factory; 
    private $objects = array(); 

    public function __get($profile) { 
     // check for object and return it if it's created before 
    } 

    public static function getInstance(){ 
     if (!self::$factory) 
      self::$factory = new self(); 
     return self::$factory; 
    } 
} 

Los mismos repite el código en cualquier momento en que necesita un objeto que es propia fábrica. Así que decidí hacer este resumen de clase de fábrica e implementar solo rutinas específicas para cada fábrica. Pero PHP no permite crear instancias de clases abstractas.

abstract class Base_Factory { 
    public static function getInstance(){ 
     if (!self::$factory) 
      self::$factory = new self(); 
     return self::$factory; 
    } 
} 

Fatal error: No se puede crear una instancia de clase abstracta Base_Factory

¿Qué haría usted?

+0

La pregunta es esencialmente: ¿cómo me libero de escribir esas funciones getInstance (y tal vez más adelante) en cada fábrica? –

Respuesta

29

En los métodos de PHP, self siempre se refiere a la clase en la que se define el método. Desde la versión 5.3.0, PHP admite el "enlace estático tardío", donde puede usar la palabra clave static para acceder a métodos estáticos anulados, así como la función get_called_class() para obtener el nombre de la clase derivada en contexto estático.

Sin embargo, su diseño tiene un defecto importante: la propiedad estática $factory definida en Base_Factory se comparte en todas las clases derivadas. Por lo tanto, la primera vez que se crea y almacena un singleton en esta propiedad, todas las demás llamadas al getInstance() devolverán el mismo objeto, sin importar qué clase derivada se use.

Se puede usar un nombre de clase de mapeo diccionario estáticas a Singleton objetos:

abstract class Base_Factory { 
    private static $_instances = array(); 
    public static function getInstance() { 
     $class = get_called_class(); 
     if (!isset(self::$_instances[$class])) { 
      self::$_instances[$class] = new $class(); 
     } 
     return self::$_instances[$class]; 
    } 
} 

Ah, una cosa más: el hecho de que está buscando una posibilidad de reutilización de código para los objetos simples podrían ser una ¡indica el hecho de que estás usando en exceso el patrón de diseño singleton! Pregúntese si las clases que planea implementar como singletons son realmente singletons y si no habrá ningún caso de uso en el que desee tener varias instancias de la clase en particular.

A menudo es mucho mejor utilizar solo un singleton que represente el "contexto de la aplicación" actual que proporciona los descriptores de acceso para los objetos que son singleton con respecto a este contexto.

+0

Gracias, pero desafortunadamente no tengo PHP> = 5.3 en mi hosting compartido en este momento. Pero voy a mantener este código para los tiempos que lo haré :). –

+1

Se olvidó de declarar $ _instances como static también – clu3

+0

@ clu3 - Gracias. –

-2

Bueno, podría hacer una comprobación para asegurarse de que la clase que llama a la función no es Base_Factory.

if(__CLASS__!='Base_Factory') 

Luego use $ this en lugar de self para referirse al objeto actual en lugar de la clase actual.

if (!$this->factory) 
     $this->factory = new self(); 
    return $this->factory; 
+1

Las funciones estáticas no tienen '$ this'. Además, '__CLASS__' siempre representa la clase donde se define una función, en su caso, siempre será 'Base_Factory', incluso en clases derivadas. –

+0

Ah, lo siento, no puede usar get_class ($ this) como alternativa. – Shane

-1

Por definición, no se puede crear una instancia de clase abstracta en PHP como cualquier otro lenguaje orientado a objetos. Entonces, tu Base_Factory debería ser una interfaz en lugar de una clase abstracta.

Del manual de PHP: "No está permitido crear una instancia de una clase que se haya definido como abstracta".

+1

Mark parece saber que no puede crear una instancia de su clase base abstracta. Hew quiere crear instancias dinámicas de clases derivadas y no abstractas del código definido en la clase base abstracta. –

+0

Sí Ferdinand, eso es preciso. –

4

PHP> = 5.3 solamente

abstract class Base_Factory { 
    protected static $factory; 
    public static function getInstance(){ 
     if (!self::$factory) { 
      $class = get_called_class(); 
      self::$factory = new $class(); 
     } 
     return self::$factory; 
    } 
} 
+0

+1 Funciona. Entonces, ¿por qué creamos un diccionario como en la respuesta aceptada de @FerdinandBeyer? Creo que solo si queremos muchas instancias singleton de la misma clase abstracta, ¿verdad? –

+1

Idea interesante, pero esto no funciona en PHP7. – SalientGreen

+1

Actualización: o en PHP 5.3 por las mismas razones que se describen en la respuesta aceptada. self :: $ factory se definirá una vez que se haya instanciado cualquier clase derivada. – SalientGreen

1

Registro de sus hijos únicos en una clase simple como esto

class Singletons { 
    static private $singleton = array(); 
    public function getSingleton($class) { 
    if (!isset(self::$singleton[$class])) { 
     self::$singleton[$class] = new $class; 
    } 
    return self::$singleton[$class]; 
    } 
} 

Entonces hacer esto

class aSingleton { 
    public $i; 
    public function test() { 
    ++$this->i; 
    echo get_class() . " called {$this->i} times\n"; 
    } 
} 

Singletons::getSingleton('aSingleton')->test(); 
Singletons::getSingleton('aSingleton')->test(); 

salida

aSingleton called 1 times 
aSingleton called 2 times 
+0

Puede ser que tengas que tener una variable de miembro estática que rastrea la única instancia de aSingleton. –

8

PHP 5.3+

abstract class Singleton 
{ 
    /** 
    * Instance 
    * 
    * @var Singleton 
    */ 
    protected static $_instance; 

    /** 
    * Constructor 
    * 
    * @return void 
    */ 
    protected function __construct() {} 

    /** 
    * Get instance 
    * 
    * @return Singleton 
    */ 
    public final static function getInstance() { 
     if (null === static::$_instance) { 
      static::$_instance = new static(); 
     } 

     return static::$_instance; 
    } 
}