2009-11-30 38 views
18

Se tu avessi una classe factory che crea nuovi oggetti di qualche tipo, e che la classe factroy è un Singleton, come questo:Estendere classe astratta Singleton

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; 
    } 
} 

Le stesse ripetizioni di codice in qualsiasi momento in cui un oggetto ha bisogno di un proprio fabbrica. Così ho deciso di rendere questa classe astratta e implementare solo routine specifiche per ogni fabbrica. Ma PHP non consente di creare un'istanza di classe astratta.

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

Fatal error: Impossibile istanziare classe astratta Base_Factory

Cosa faresti?

+0

La domanda in sostanza è: come posso liberarmi dallo scrivere quelle funzioni getInstance (e forse più tardi in più) in ogni fabbrica? –

risposta

29

Nei metodi PHP, self riferisce sempre alla classe in cui è definito il metodo. Dalla versione 5.3.0, PHP supporta "late static binding", dove è possibile utilizzare la parola chiave static per accedere ai metodi statici sovrascritti, nonché la funzione get_called_class() per ottenere il nome della classe derivata nel contesto statico.

Tuttavia, il design ha un difetto importante: la proprietà statica $factory definita in Base_Factory è condivisa tra tutte le classi derivate. Pertanto, la prima volta che un singleton viene creato e archiviato in questa proprietà, tutte le altre chiamate a getInstance() restituiranno lo stesso oggetto, indipendentemente dalla classe derivata utilizzata.

si potrebbe usare un nomi delle classi mappatura dizionario statici per Singleton oggetti:

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]; 
    } 
} 

Oh, un'altra cosa: il fatto che siete alla ricerca di una possibilità di ri-utilizzare il codice per gli oggetti Singleton potrebbe essere un spunto dal fatto che stai sfruttando eccessivamente il modello di design singleton! Chiediti se le classi che stai pianificando di implementare come singletons sono davvero singleton e se non ci saranno casi d'uso in cui potresti volere avere più istanze di una particolare classe.

Spesso è molto meglio utilizzare solo un singleton che rappresenta l'attuale "contesto applicativo" che fornisce accessor per oggetti che sono singleton rispetto a questo contesto.

+0

Grazie, ma sfortunatamente non ho PHP> = 5.3 sul mio hosting condiviso al momento. Ma terrò questo codice per le volte che lo farò :). –

+1

Hai dimenticato di dichiarare $ _stance anche statico – clu3

+0

@ clu3 - Grazie. –

-2

Bene, è possibile eseguire un controllo per assicurarsi che la classe che chiama la funzione non sia Base_Factory.

Quindi utilizzare $ this anziché self per fare riferimento all'oggetto corrente anziché alla classe corrente.

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

Le funzioni statiche non hanno '$ this'. Inoltre, '__CLASS__' rappresenta sempre la classe in cui è definita una funzione, nel tuo caso, sarà sempre 'Base_Factory', anche nelle classi derivate. –

+0

Ah scusatemi, non potete nemmeno usare get_class ($ this) come alternativa. – Shane

-1

Per definizione, la classe astratta non può essere istanziata in PHP come qualsiasi altro linguaggio orientato agli oggetti. Quindi il tuo Base_Factory dovrebbe essere l'interfaccia invece della classe astratta.

Dal manuale PHP: "Non è consentito creare un'istanza di una classe che è stata definita come astratta."

+1

Mark sembra sapere che non può istanziare la sua classe base astratta. Hew vuole istanziare dinamicamente classi derivate, non astratte dal codice definito nella classe base astratta. –

+0

Sì, Ferdinando, è preciso. –

4

PHP> = 5.3 solo

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 Funziona. Quindi, perché dovremmo creare un dizionario come nella risposta accettata da @FerdinandBeyer? Penso solo se vogliamo molte istanze di singleton della stessa classe astratta, giusto? –

+1

Idea interessante, ma questo non funziona in PHP7. Aggiornamento – SalientGreen

+1

: o in PHP 5.3 per gli stessi motivi descritti nella risposta accettata. self :: $ factory verrà definito dopo l'istanza di una classe derivata. – SalientGreen

1

Registrare i single in una classe semplice come questo

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

Poi fare questo

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(); 

uscita

aSingleton called 1 times 
aSingleton called 2 times 
+0

Potrebbe essere necessario avere una variabile membro statica che tiene traccia la singola istanza di 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; 
    } 
}